用 canvas 制作简易版组态 联动连接工具

效果如下,拖动任意一个,连线会随着动,
(连线连的比较随意,后期换别的版本了,就没继续做下去)

1.1 支持任意添加
1.2 支持右键删除
1.3 支持联动

使用说明:
1.点击图片添加图片
2.进入连线模式侯才可以连线

这里写图片描述

<!DOCTYPE html>
<html>
<head>
    <title>鼠标滚轮</title>
    <meta charset="utf-8">
    <style>
        canvas {
            border: 1px dashed black;
        }
    </style>
    <script src="js/fabric.js"></script>
    <script src="js/jquery-1.11.3.js"></script>
    <script src="js/jquery.mousewheel.js"></script>
    <script src="contextMenu/jquery.ui.position.min.js" type="text/javascript"></script>
    <script src="contextMenu/jquery.contextMenu.js" type="text/javascript"></script>
    <link href="contextMenu/jquery.contextMenu.css" rel="stylesheet" type="text/css" />
</head>
<body>

<div>非连接模式下,按住alt 才可拖动画布</div>
    <canvas id="canvas" width="600" height="600"></canvas>
    <div id="contextmenu-output"></div>
<div>
    <button id="addPic" onclick="addPic()">图片</button>
    <button id="addLine" onclick="addLine()">连线模式</button>
</div>


<script>
    var canvas;

    //是否拖动
    var panning = false;

    //连线模式
    var ligature = false;
    var lineNum;
    var selectObj;

    canvas = new fabric.Canvas('canvas');

    //鼠标按下
    canvas.on('mouse:down', function (e) {

        if(ligature){ //连线模式

            //点击位置坐标
            var pointering = canvas.getPointer(event.originalEvent);
            var inner = false;
            var objects = canvas.getObjects();
            var selected;

            //定义被点击物体的信息
            var minLeft;
            var maxLeft;
            var minTop;
            var maxTop;
            var w;
            var h;

            for (var i = objects.length - 1; i >= 0; i--) {
                var obj = objects[i];
                minLeft = obj.left;
                maxLeft = obj.left+(obj.width)*obj.scaleX;
                minTop = obj.top;
                maxTop = obj.top+(obj.height)*obj.scaleY;
                w = obj.width*obj.scaleX;
                h = obj.height*obj.scaleY;

                if(pointering.x>minLeft && pointering.x<maxLeft && pointering.y>minTop && pointering.y<maxTop){
                    inner = true;
                    selected = obj;
                    break;
                }
            }

            if(inner){  //点击到物体上后
                if (lineNum == undefined){  //第一次点击到物体上先记录信息
                    lineNum = [minLeft,maxLeft,minTop,maxTop,w,h];
                    selectObj = selected;
                }else{                        //第二次点击后画线
                    var startX,startY,endX,endY;
                    var line;
                    if( lineNum[1]<minLeft || lineNum[0]>=maxLeft ){//连接物在左侧或右侧
                        if(lineNum[1]<minLeft){
                            startX = lineNum[1];
                            startY = lineNum[2]+lineNum[5]/2;
                            endX = minLeft;
                            endY = minTop+h/2;
                        }else{
                            startX = lineNum[0];
                            startY =  lineNum[2] + lineNum[5]/2;
                            endX = maxLeft;
                            endY = minTop + h/2;
                        }
                        var middleX = (endX - startX)/2  + startX;
                        var middleY1 = startY;
                        var middleY2 = endY;

                        line = makeLine('M '+startX+' '+startY+' L '+middleX+' '+middleY1+' L '+middleX+' '+middleY2+' L '+endX+' '+endY,[selected,selectObj]);

                    }else {
                        if(lineNum[2]>minTop){
                            startX = lineNum[0]+ lineNum[4]/2;
                            startY = lineNum[2];
                            endX = minLeft+ w/2;
                            endY = maxTop;
                        }else if(lineNum[3]<minTop){
                            startX = lineNum[0]+ lineNum[4]/2;
                            startY = lineNum[3];
                            endX = minLeft+ w/2;
                            endY = minTop;
                        }

                        middleY = startY+ (endY - startY)/2;
                        middleX1 = startX;
                        middleX2 = endX;

                        line = makeLine('M '+startX+' '+startY+' L '+middleX1+' '+middleY+' L '+middleX2+' '+middleY+' L '+endX+' '+endY,[selected,selectObj]);
                    }

                    lineNum = undefined;
                    canvas.add(line);
                    selected.lineGroup.push(line);
                    selectObj.lineGroup.push(line);
                }
            }else{   //点击空白,清空以一次点击信息
                lineNum = undefined;
            }

        }else {  //非连接模式下   可以拖动画布
            //按住alt键才可拖动画布
            if(e.e.altKey) {
                panning = true;
                canvas.selection = false;
            }
        }


        console.log('数据结构:');
        console.log(canvas._objects);
        console.log('________________');
    });

    //鼠标抬起
    canvas.on('mouse:up', function (e) {
        panning = false;
        canvas.selection = true;

        var currObj = e.target;
        if(currObj && !ligature && currObj.lineGroup!=''){

            //被拖动物体的lineGroup
            var currObjLineGroup = currObj.lineGroup;

            if(currObjLineGroup){
                //遍历拖动的图片上的lineGroup
                for( var i = (currObjLineGroup.length - 1); i >= 0; i-- ){
                    var currentLine = currObjLineGroup[i];
                    //每根线对应两个图片
                    var innerPic = currObjLineGroup[i].pic;
                    //用于存放需要重新绘制线的两个物体
                    var drawObj = [[],[]];
                    var lineObjGroup = [];

                    //去掉这两个图片里面关于这条线的数据
                    for( var j = 1; j>=0; j--){
                        //图片里面对应的lineGroup
                        for( var m = (innerPic[j].lineGroup.length -1);m>=0;m--){
                            if(innerPic[j].lineGroup[m] == currentLine){
                                innerPic[j].lineGroup.splice(m,1);
                            }
                        }
                        lineObjGroup.push(innerPic[j]);
                        //     0        1        2       3    4  5
                        //  minleft, maxleft, minTop, maxTop, w, h
                        drawObj[j] = [innerPic[j].left,innerPic[j].left+(innerPic[j].width)*innerPic[j].scaleX,innerPic[j].top,innerPic[j].top+(innerPic[j].height)*innerPic[j].scaleY,innerPic[j].width*innerPic[j].scaleX,innerPic[j].height*innerPic[j].scaleY];
                    }

                    //去掉旧线
                    canvas.remove(currentLine);

                    //根据新位置画线
                    var startX,startY,endX,endY;
                    var line;
                    if( drawObj[0][1]<drawObj[1][0] ||  drawObj[0][0]>= drawObj[1][1] ){
                        if(drawObj[0][1]<drawObj[1][0]){
                            startX = drawObj[0][1];
                            startY = drawObj[0][2]+drawObj[0][5]/2;
                            endX = drawObj[1][0];
                            endY = drawObj[1][2]+drawObj[1][5]/2;
                        }else{
                            startX = drawObj[0][0];
                            startY = drawObj[0][2] + drawObj[0][5]/2;
                            endX = drawObj[1][1];
                            endY = drawObj[1][2] + drawObj[1][5]/2;
                        }

                        var middleX = (endX - startX)/2  + startX;
                        var middleY1 = startY;
                        var middleY2 = endY;

                        line = makeLine('M '+startX+' '+startY+' L '+middleX+' '+middleY1+' L '+middleX+' '+middleY2+' L '+endX+' '+endY,lineObjGroup);
                    }else {
                        if(drawObj[0][2]>drawObj[1][2]){
                            startX = drawObj[0][0]+ drawObj[0][4]/2;
                            startY = drawObj[0][2];
                            endX = drawObj[1][0]+ drawObj[1][4]/2;
                            endY = drawObj[1][3];
                        }else if(drawObj[0][3]<drawObj[1][2]){
                            startX = drawObj[0][0]+ drawObj[0][4]/2;
                            startY = drawObj[0][3];
                            endX = drawObj[1][0]+ drawObj[1][4]/2;
                            endY = drawObj[1][2];
                        }

                        middleY = startY+ (endY - startY)/2;
                        middleX1 = startX;
                        middleX2 = endX;

                        line = makeLine('M '+startX+' '+startY+' L '+middleX1+' '+middleY+' L '+middleX2+' '+middleY+' L '+endX+' '+endY,lineObjGroup);
                    }
                    canvas.add(line);

                    lineObjGroup[0].lineGroup.push(line);
                    lineObjGroup[1].lineGroup.push(line);
                }
            }
        }
    });

    //鼠标移动
    canvas.on('mouse:move', function (e) {

        if (panning && e && e.e) {
            var delta = new fabric.Point(e.e.movementX, e.e.movementY);
            canvas.relativePan(delta);
        }

    });

    function makeLine(info,pic) {

        return new fabric.Path(info, {
            fill: 'transparent',
            stroke: 'red',
            strokeWidth: 5,
            selectable: false,
            type: 'line',
            pic: pic
        });
    }

    function addPic() {
        fabric.Image.fromURL('timg.jpg', function(oImg) {
            oImg.scale(0.1);//图片缩小10倍
            oImg.hasBorders = false;
            oImg.lineGroup = [];
            oImg.type='pic';
            canvas.add(oImg);
        });
    }

    //鼠标滚轮监听
    $(".upper-canvas").mousewheel(function(event) {
        var zoom = (event.deltaY > 0 ? 0.1 : -0.1) + canvas.getZoom();
        zoom = Math.max(0.1,zoom); //最小为原来的1/10
        zoom = Math.min(3,zoom); //最大是原来的3倍
        var zoomPoint = new fabric.Point(event.pageX, event.pageY);
        canvas.zoomToPoint(zoomPoint, zoom);
    });

    //在canvas上层对象上添加右键事件监听
    $(".upper-canvas").contextmenu(onContextmenu);

    //初始化右键菜单
    $.contextMenu({
        selector: '#contextmenu-output',
        trigger: 'none',
        build: function($trigger, e) {
            //构建菜单项build方法在每次右键点击会执行
            return {
                callback: contextMenuClick,
                items: contextMenuItems
            };
        },
    });

    //右键点击事件响应
    function onContextmenu(event) {
        var pointer = canvas.getPointer(event.originalEvent);
        var objects = canvas.getObjects();
        for (var i = objects.length - 1; i >= 0; i--) {
            var object = objects[i];
            //判断该对象是否在鼠标点击处
            if (canvas.containsPoint(event, object)) {
                //选中该对象
                canvas.setActiveObject(object);
                //显示菜单
                showContextMenu(event, object);
                continue;
            }
        }

        //阻止系统右键菜单
        event.preventDefault();
        return false;
    }

    //右键菜单项点击
    function showContextMenu(event, object) {
        //定义右键菜单项
        contextMenuItems = {
            "delete": {name: "删除", icon: "delete", data: object}
        };
        //右键菜单显示位置
        var position = {
            x: event.clientX,
            y: event.clientY
        }
        $('#contextmenu-output').contextMenu(position);
    }

    //右键菜单项点击
    function contextMenuClick(key, options) {
        if(key == "delete") {
            //得到对应的object并删除
            var object = contextMenuItems[key].data;
            if(object.type == 'line'){
                var objects = object.pic;
                for (var i = 0; i < 2; i++) {
                    var innerLine = objects[i].lineGroup;
                    for( var j = 0;j< innerLine.length;j++ ){
                        if(innerLine[j] == object){
                            innerLine.splice(j,1);
                        }
                    }
                }
            }else{
                var objects = object.lineGroup;
                //首先遍历删除图片内对应的连线有哪些
                for (var i = (objects.length-1); i >=0 ; i--) {
                    var deleteLine = objects[i];
                    var innerPic = deleteLine.pic;

                    //遍历对应线两端连接的图片
                    for( var j =0; j < 2; j++ ){
                        //删除另一端图片内所包含的该线的数据
                        for( var m =0;m<innerPic[j].lineGroup.length;m++){
                            if(innerPic[j].lineGroup[m] == deleteLine){
                                innerPic[j].lineGroup.splice(m,1);
                            }
                        }
                    }

                    canvas.remove(deleteLine);
                }
            }
            canvas.remove(object);
        }
    }

    //连线模式
    function addLine() {
        ligature = !ligature;

        if(ligature){

            $('#addLine').text('退出连接模式!');
            $('#canvas').css('background','#ff9e3d');
            $('#addPic').attr({"disabled":"disabled"});

            lineNum = undefined;

            var objects = canvas.getObjects();
            for (var i = objects.length - 1; i >= 0; i--) {
                objects[i].selectable = false;
            }
        }else{

            $('#addLine').text('连接模式');
            $('#canvas').css('background','#fff');
            $('#addPic').attr({"disabled":false});

            var objects = canvas.getObjects();
            for (var i = objects.length - 1; i >= 0; i--) {
                objects[i].selectable = true;
            }
        }
    }
</script>
</body>
</html>
发布了38 篇原创文章 · 获赞 5 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/weixin_39423672/article/details/81185488