Edit in JSFiddle

//Aliases
let Container = PIXI.Container,
    Application = PIXI.Application,// Fixed!! use Canvas force!!
    Sprite = PIXI.Sprite,
    Graphics = PIXI.Graphics;

let renderer = new Application(window.innerWidth, window.innerHeight, {
        antialias: true,//这抗锯齿一开整个世界都变了  => use Canvas no Webgl!!!
        forceFXAA: true,//For WebglRender AA
        backgroundColor: 0x1099bb
    }),//Todo=> parameter
    stage = renderer.stage,
    edgeContainer = new Container(),
    arrowContainer = new Container(),
    nodeContainer = new Container(),//节点在边之上
    dragContainer = new Container();//拖拽的节点处于最高层级
    //Canvas(defalut is Webgl) Render test
    renderer.renderer = new PIXI.CanvasRenderer(window.innerWidth, window.innerHeight, {
        backgroundColor: 0x1099bb
    })

document.body.appendChild(renderer.view);

const SCALE_MAX = 24, SCALE_MIN = 0.1;//For scale limmit
let nodeWidth=30;//默认值
let point = {};//Todo 这里以后指针的形状也可以自定义
let movePosBegin = {};

//先试试分开保存,便于搜索,先这样吧
let nodeList = {},
    edgeList = {},//保存边引用
    arrowList = {},//保存箭头信息
    edgeInfoList = {};//保存边信息

let testOrder = true; //Use to test z-index


//Result/Goal
PiCi({
    container: document.getElementById('cy'),
    elements: [//先node再edge
        { data: { id: 'a', x: 342, y: 42 ,width:40} },
        { data: { id: 'b', x: 212, y: 312 } },
        { data: { id: 'c', x: 242, y: 642 } },
        { data: { id: 'd', x: 412, y: 112 } },
        {
            data: {
                id: 'ab',
                source: 'a',
                target: 'b',
                targetShape: 'triangle',
                sourceShape: 'circle',
            }
        },
        {
            data: {
                id: 'cd',
                source: 'c',
                target: 'd',
                targetShape: 'triangle',
                sourceShape: 'circle',
            }
        },
        {
            data: {
                id: 'bc',
                source: 'b',
                target: 'c',
                targetShape: 'triangle',
                sourceShape: 'circle',
            }
        },
    ],
})


function PiCi(opts) {
    opts = Object.assign({}, opts);
    //Extrac nodes/edges information from opts
    let elements = opts.elements;
    if (!elements) elements = [];
    if (elements.length > 0) {
        for (let i = 0, l = elements.length; i < l; i++) {
            let data = elements[i].data,
                id = data.id;

            //Simple check
            if (data == null) data = {};
            if (id == null) {//Id is neccesary
                //Check whether id is already exists
                if ((nodeList[id] != undefined) || edgeList[id] != undefined) {
                    console.error("id已存在");
                    break;
                } else {
                    console.error("id是必须参数");
                    break;
                }
            }
            
            if (data.source && data.target) {
                //Save this edge's info
                edgeInfoList[data.id] = data;

                //Get position info
                let source = nodeList[data.source];
                let target = nodeList[data.target];
            
                let newSourcePos, newTargetPos;
                //别着急画线啊,先画箭头和椭圆(Arrow first)
                if (data.targetShape) {
                    //Todo => nodeWidth
                    newTargetPos = drawTargetShape(data.id, data.targetShape, source, target);
                }
                if (data.sourceShape) {
                    newSourcePos = drawSourceShape(data.id, data.sourceShape, source, target);
                }

                let tempSourcePos = newSourcePos ? newSourcePos : source;
                let tempTargetPos = newTargetPos ? newTargetPos : target;

                //Draw edge
                let line = new Graphics();
                line.lineStyle(4, 0xFFFFFF, 1);

                line.moveTo(tempSourcePos.x, tempSourcePos.y);
                //先直线版
                line.lineTo(tempTargetPos.x, tempTargetPos.y);
                //line.quadraticCurveTo((tempSourcePos.x + tempTargetPos.x) / 2, (tempSourcePos.y + tempTargetPos.y) / 2 + 100, tempTargetPos.x, tempTargetPos.y);
                
                edgeList[data.id] = line;//保存边引用

                edgeContainer.addChild(line);

            } else {
                //Add node to nodeList
                nodeList[data.id] = data;

                //Draw node
                let circle = new Graphics();

                //Test z-index   no use ----
                testOrder = !testOrder;
                if (testOrder) {
                    circle.beginFill(0x66CCFF);
                } else {
                    circle.beginFill(0x000000);
                }
                //-------
                let width = nodeWidth;
                if(data.width)width=data.width;
                circle.drawCircle(0, 0, width);
                circle.endFill();
                circle = setNode(circle, data.id);

                //Move the graph to its designated position
                //Todo => Node坐标随机分布
                circle.x = data.x;
                circle.y = data.y;
                
                nodeContainer.addChild(circle);
            }
        }
    }
    //层级顺序
    stage.addChild(edgeContainer);
    stage.addChild(arrowContainer);
    stage.addChild(nodeContainer);
    stage.addChild(dragContainer);
}

function setNode(graph, id) {

    let onDragStart = function (event) {
        // store a reference to the data
        // the reason for this is because of multitouch
        // we want to track the movement of this particular touch
        event.stopPropagation();
        this.data = event.data;
        this.dragging = true;
    }

    let onDragEnd = function () {
        this.dragging = false;
        // set the interaction data to null
        this.data = null;
        //归位
        dragContainer.removeChild(this);
        nodeContainer.addChild(this);
    }

    let onDragMove = function () {
        if (this.dragging) {
            let newPosition = this.data.getLocalPosition(this.parent);
            this.x = newPosition.x;
            this.y = newPosition.y;
            updateEdge(id, newPosition);//闭包的缘故,id是能访问得到的
            nodeContainer.removeChild(this);
            dragContainer.addChild(this);
        }
    }

    let updateEdge = function (id, newPos) {
        for (let element in edgeInfoList) {//一个节点可能连接着多根线
            if (edgeInfoList[element].target === id) {
                drawNewEdge(edgeInfoList[element], true, newPos);
            } else if (edgeInfoList[element].source === id) {
                drawNewEdge(edgeInfoList[element], false, newPos);
            }
        };
    }

    let drawNewEdge = function (element, targetFlag, newPos) {
        let oldLine = edgeList[element.id];//在线的引用保存对象里找到线
        edgeContainer.removeChild(oldLine);//删除线重新画

        //Get position info
        let sourcePos = targetFlag ? nodeList[element.source] : newPos,//起点(node坐标)
            targetPos = targetFlag ? newPos : nodeList[element.target];//终点(node坐标)

        let newSourcePos, newTargetPos;
        //别着急画线啊,先画箭头和椭圆
        if (element.targetShape) {
            newTargetPos = drawTargetShape(element.id, element.targetShape, sourcePos, targetPos, nodeWidth);
        }
        if (element.sourceShape) {
            newSourcePos = drawSourceShape(element.id, element.sourceShape, sourcePos, targetPos, nodeWidth);
        }

        let tempSourcePos = newSourcePos ? newSourcePos : sourcePos;
        let tempTargetPos = newTargetPos ? newTargetPos : targetPos;//todo 多余了其实

        //Draw edge
        let line = new Graphics();
        line.lineStyle(4, 0xFFFFFF, 1);
        line.moveTo(tempSourcePos.x, tempSourcePos.y);
        line.lineTo(tempTargetPos.x, tempTargetPos.y);

        //Save position change
        if (targetFlag) {
            //保存修改了的target Node坐标
            nodeList[element.target].x = newPos.x;
            nodeList[element.target].y = newPos.y;
        } else {
            //保存修改了的source Node坐标
            nodeList[element.source].x = newPos.x;
            nodeList[element.source].y = newPos.y;
        }

        edgeList[element.id] = line;//保存边引用
        edgeContainer.addChild(line);
    }

    graph.interactive = true;
    // this button mode will mean the hand cursor appears when you roll over the bunny with your mouse
    graph.buttonMode = true;
    graph
        .on('pointerdown', onDragStart)
        .on('pointerup', onDragEnd)
        .on('pointerupoutside', onDragEnd)
        .on('pointermove', onDragMove);

    return graph;
}

function drawSourceShape(id, shape, sourcePos, targetPos) {
    let nodeRadius = nodeWidth;
    if(sourcePos.width)nodeRadius = sourcePos.width;
    //贴一起了就别显示啦
    if ((Math.abs(sourcePos.y - targetPos.y) < nodeRadius * 1.5) &&
        (Math.abs(sourcePos.x - targetPos.x) < nodeRadius * 1.5)) {
            nodeRadius = 0;
    }
    switch (shape) {
        case 'circle':
            let angle = Math.atan(Math.abs(sourcePos.y - targetPos.y) / Math.abs(sourcePos.x - targetPos.x))
            let circleWidth = nodeRadius / 2;
            //posX和posY就是circle的最终中心坐标
            let posX = (nodeRadius + circleWidth) * Math.cos(angle),
                posY = (nodeRadius + circleWidth) * Math.sin(angle);

            //分类讨论target和source的相对左右位置
            if (sourcePos.x > targetPos.x) {//source节点在右边
                posX = sourcePos.x - posX;
            } else {
                posX = sourcePos.x + posX;
            }
            if (sourcePos.y > targetPos.y) {//source节点在上边
                posY = sourcePos.y - posY;
            } else {
                posY = sourcePos.y + posY;
            }

            //Draw circle
            let circle = new Graphics();
            circle.beginFill(0x66CCFF);

            circle.drawCircle(0, 0, circleWidth);
            circle.endFill();

            circle.x = posX;
            circle.y = posY;

            if (!arrowList[id]) arrowList[id] = {};
            //remove oldArrow first
            if (arrowList[id].sourceArrow) arrowContainer.removeChild(arrowList[id].sourceArrow);
            //save newArrow
            arrowList[id].sourceArrow = circle;
            arrowContainer.addChild(circle);

            return {
                x: posX,
                y: posY
            }
    }
}

function drawTargetShape(id, shape, sourcePos, targetPos) {
    let nodeRadius = nodeWidth;
    if(targetPos.width)nodeRadius = targetPos.width;
    //贴一起了就别显示啦
    if ((Math.abs(sourcePos.y - targetPos.y) < nodeRadius * 1.5) &&
        (Math.abs(sourcePos.x - targetPos.x) < nodeRadius * 1.5)) {
            nodeRadius = 0;
    }
    switch (shape) {
        case 'triangle':
            //这个三角形默认按顶角为50°,两个底角为65°来算,两边长先按一半nodeWidth来算吧
            //先画出来再想抽象的事
            let topAngle = Math.PI / 180 * 50,//角度转弧度,注意Math的那些方法的单位是弧度
                sideEdge = nodeRadius,//瞅着合适,先凑合
                halfBottomEdge = Math.sin(topAngle / 2) * sideEdge,
                centerEdge = Math.cos(topAngle / 2) * sideEdge;
            //angle是一样的,先按node中心算,arrow中心算之后再说,先todo(直线版看出不这个问题,曲线就崩了)
            let angle = Math.atan(Math.abs(sourcePos.y - targetPos.y) / Math.abs(sourcePos.x - targetPos.x));
            let beginPosX = nodeRadius * Math.cos(angle),
                beginPosY = nodeRadius * Math.sin(angle),
                pos1X, pos1Y, pos2X, pos2Y,
                centerX = (nodeRadius + centerEdge) * Math.cos(angle),
                centerY = (nodeRadius + centerEdge) * Math.sin(angle);

            pos1X = pos2X = Math.sin(angle) * halfBottomEdge;
            pos1Y = pos2Y = Math.cos(angle) * halfBottomEdge;//简单的几何知识(手动抽搐😖)

            //还需要分类讨论target和source的左右位置的各种情况
            //1234代表target相对source所在象限
            if (sourcePos.x > targetPos.x) {//source节点在右
                if (sourcePos.y > targetPos.y) {//下 ----> 1
                    beginPosX = targetPos.x + beginPosX;
                    beginPosY = targetPos.y + beginPosY;

                    centerX = targetPos.x + centerX;
                    centerY = targetPos.y + centerY;

                    pos1X = centerX + pos1X;
                    pos1Y = centerY - pos1Y;//+ -

                    pos2X = centerX - pos2X;
                    pos2Y = centerY + pos2Y;//- +
                } else {//上 ----> 4
                    beginPosX = targetPos.x + beginPosX;
                    beginPosY = targetPos.y - beginPosY;

                    centerX = targetPos.x + centerX;
                    centerY = targetPos.y - centerY;

                    pos1X = centerX + pos1X;
                    pos1Y = centerY + pos1Y;//+ +

                    pos2X = centerX - pos2X;
                    pos2Y = centerY - pos2Y;//- -
                }

            } else {//source节点在左
                if (sourcePos.y > targetPos.y) {//下 ----> 2
                    beginPosX = targetPos.x - beginPosX;
                    beginPosY = targetPos.y + beginPosY;

                    centerX = targetPos.x - centerX;
                    centerY = targetPos.y + centerY;

                    pos1X = centerX - pos1X;
                    pos1Y = centerY - pos1Y;//- -

                    pos2X = centerX + pos2X;
                    pos2Y = centerY + pos2Y;//+ +
                } else {//上 ----> 3
                    beginPosX = targetPos.x - beginPosX;
                    beginPosY = targetPos.y - beginPosY;

                    centerX = targetPos.x - centerX;
                    centerY = targetPos.y - centerY;

                    pos1X = centerX - pos1X;
                    pos1Y = centerY + pos1Y;//- +

                    pos2X = centerX + pos2X;
                    pos2Y = centerY - pos2Y;//+ -
                }
            }

            //Draw triangle
            let triangle = new Graphics();

            triangle.beginFill(0x66CCFF);
            triangle.lineStyle(0, 0x66CCFF, 1);
            triangle.moveTo(beginPosX, beginPosY);
            triangle.lineTo(pos1X, pos1Y);
            triangle.lineTo(pos2X, pos2Y);
            triangle.endFill();

            if (!arrowList[id]) arrowList[id] = {};
            //remove oldArrow first
            if (arrowList[id].targetArrow) arrowContainer.removeChild(arrowList[id].targetArrow);
            //save newArrow
            arrowList[id].targetArrow = triangle;
            arrowContainer.addChild(triangle);

            return {
                x: centerX,
                y: centerY
            }
    }
}

// Scale/Zoom
renderer.view.addEventListener('wheel', function (e) {
    if (e.deltaY < 0) {
        zooming(true, e.pageX, e.pageY);
    } else {
        zooming(false, e.pageX, e.pageY);
    }
});

function zooming(zoomFlag, x, y) {
    //Current scale    
    let scale = stage.scale.x;
    let point = toLocalPos(x, y);
    //Zooming    
    if (zoomFlag) {
        if (scale < SCALE_MAX) {
            scale += 0.1;
            //moving      
            stage.position.set(stage.x - (point.x * 0.1), stage.y - (point.y * 0.1))
        }
    } else {
        if (scale > SCALE_MIN) {
            scale -= 0.1;
            //moving
            stage.position.set(stage.x - (point.x * -0.1), stage.y - (point.y * -0.1))
        }
    }
    stage.scale.set(scale, scale);
}

function drawCircle(x, y, r = 30) {
    let circle = new Graphics();
    circle.beginFill(0x000000, 0.2);

    circle.drawCircle(0, 0, r);
    circle.endFill();
    let localPos = toLocalPos(x, y);
    circle.x = localPos.x;
    circle.y = localPos.y;
    point.circle = circle;
    dragContainer.addChild(circle);
}

// Drag/Move
let startMousePos = {};

stage.hitArea = new PIXI.Rectangle(0, 0, window.innerWidth, window.innerHeight);
stage.interactive = true;
stage.buttonMode = true;

stage.on('pointerdown', stagePointerDown)
     .on('pointerup', stagePointerUp)
     .on('pointerupoutside', stagePointerUp)
     .on('pointermove', stagePointerMove);

function stagePointerDown(event) {
    this.dragging = true;
    movePosBegin.x = stage.x;
    movePosBegin.y = stage.y;
    let newPosition = event.data.global;
    let x = newPosition.x;
    let y = newPosition.y;
    startMousePos.x = x; startMousePos.y = y;
    //Draw circle
    let r = 30 / stage.scale.x;
    drawCircle(x, y, r);
}

function stagePointerUp(event) {
    this.dragging = false;
    //Remove  circle
    if (point.circle) dragContainer.removeChild(point.circle);
}

function stagePointerMove(event) {
    if (this.dragging) {
        //Move  circle
        let newPosition = event.data.global;
        let x = newPosition.x;
        let y = newPosition.y;

        //Remove  circle first
        if (point.circle) dragContainer.removeChild(point.circle);
        //Redraw circle
        //Current scale    
        let scale = stage.scale.x;
        let r = 30 / scale;
        drawCircle(x, y, r);

        //需要注意这里的差值必须要拿global坐标来算而不是to stageLocalPos来算
        //因为stage.x/y按照的是global坐标来移动的
        let offsetX = x - startMousePos.x,//差值
            offsetY = y - startMousePos.y;

        stage.x = movePosBegin.x + offsetX;
        stage.y = movePosBegin.y + offsetY;//修正差值
    }
}

function toLocalPos(x, y) {
    let mouse = new PIXI.Point(x, y);
    let localPos = stage.toLocal(mouse);
    return localPos;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.5.1/pixi.min.js"></script>