jsPlumb + Panzoom 无限可放置画布

问题描述

我创建了一个 codepen,它使用 jquery ui droppable(用于拖放)、jsPlumb(用于流程图)和 Panzoom(平移和缩放)来创建流程图构建器。您可以将列表项从可拖动容器(第一列)拖动到流程图(第二列),然后使用点连接这些项以创建流程图。 #flowchart 是一个 Panzoom 目标,同时启用了平移和缩放。这一切正常。

但是,我希望 #flowchart div 始终跨越流程图包装器的整个区域,即 #flowchart 应该是一个无限的画布,支持平移、缩放并且是一个可放置的容器。

它应该具有与 flowchart-builder-demo 相同的效果。画布是无限的,您可以在其中从右栏中拖放项目(问题、操作、输出)。

关于如何实现这一点的任何指示(如相关事件或多个 panzoom 元素和/或 css 更改)将不胜感激。

const BG_SRC_TGT = "#2C7BE5";
const HEX_SRC_ENDPOINT = BG_SRC_TGT;
const HEX_TGT_ENDPOINT = BG_SRC_TGT;
const HEX_ENDPOINT_HOVER = "#fd7e14";
const HEX_CONNECTOR = "#39afd1";
const HEX_CONNECTOR_HOVER = "#fd7e14";

const connectorPaintStyle = {
    strokeWidth: 2,stroke: HEX_CONNECTOR,joinstyle: "round",outlinestroke: "white",outlinewidth: 1
},connectorHoverStyle = {
        strokeWidth: 3,stroke: HEX_CONNECTOR_HOVER,outlinewidth: 2,outlinestroke: "white"
    },endpointHoverStyle = {
        fill: HEX_ENDPOINT_HOVER,stroke: HEX_ENDPOINT_HOVER
    },sourceEndpoint = {
        endpoint: "Dot",paintStyle: {
            stroke: HEX_SRC_ENDPOINT,fill: "transparent",radius: 4,strokeWidth: 3
        },isSource: true,connector: ["Flowchart",{ stub: [40,60],gap: 8,cornerRadius: 5,alwaysRespectStubs: true }],connectorStyle: connectorPaintStyle,hoverPaintStyle: endpointHoverStyle,connectorHoverStyle: connectorHoverStyle,dragOptions: {},overlays: [
            ["Label",{
                location: [0.5,1.5],label: "Drag",cssClass: "endpointSourceLabel",visible: false
            }]
        ]
    },targetEndpoint = {
        endpoint: "Dot",paintStyle: {
            fill: HEX_TGT_ENDPOINT,radius: 5
        },maxConnections: -1,dropOptions: { hoverClass: "hover",activeClass: "active" },isTarget: true,{ location: [0.5,-0.5],label: "Drop",cssClass: "endpointTargetLabel",visible: false }]
        ]
    };

const getUniqueId = () => Math.random().toString(36).substring(2,8);

// Setup jquery ui draggable,droppable

$("li.list-group-item").draggable({
    helper: "clone",zIndex: 100,scroll: false,start: function (event,ui) {
        var width = event.target.getBoundingClientRect().width;
        $(ui.helper).css({
            'width': Math.ceil(width)
        });
    }
});

$('#flowchart').droppable({
    hoverClass: "drop-hover",tolerance: "pointer",drop: function (event,ui) {
        var helper = $(ui.helper);
        var fieldId = getUniqueId();

        var offset = $(this).offset(),x = event.pageX - offset.left,y = event.pageY - offset.top;
        helper.find('div.field').clone(false)
            .animate({ 'min-height': '40px',width: '180px' })
            .css({ position: 'absolute',left: x,top: y })
            .attr('id',fieldId)
            .appendTo($(this)).fadeIn('fast',function () {
                var field = $("#" + fieldId);

                jsPlumbInstance.draggable(field,{
                    containment: "parent",scroll: true,grid: [5,5],stop: function (event,ui) {
                    }
                });

                field.addClass('panzoom-exclude');
                var bottomEndpoints = ["BottomCenter"];
                var topEndPoints = ["TopCenter"];
                addEndpoints(fieldId,bottomEndpoints,topEndPoints);
                jsPlumbInstance.revalidate(fieldId);
            });

    }
});

const addEndpoints = (toId,sourceAnchors,targetAnchors) => {
    for (var i = 0; i < sourceAnchors.length; i++) {
        var sourceUUID = toId + sourceAnchors[i];
        jsPlumbInstance.addEndpoint(toId,sourceEndpoint,{ anchor: sourceAnchors[i],uuid: sourceUUID });
    }
    for (var j = 0; j < targetAnchors.length; j++) {
        var targetUUID = toId + targetAnchors[j];
        jsPlumbInstance.addEndpoint(toId,targetEndpoint,{ anchor: targetAnchors[j],uuid: targetUUID });
    }
    $('.jtk-endpoint').addClass('panzoom-exclude');
}

// Setup jsPlumbInstance

var jsPlumbInstance = jsPlumb.getInstance({
    DragOptions: { cursor: 'pointer',zIndex: 12000 },ConnectionOverlays: [
        ["Arrow",{ location: 1 }],["Label",{
            location: 0.1,id: "label",cssClass: "aLabel"
        }]
    ],Container: 'flowchart'
});


// Setup Panzoom
const elem = document.getElementById('flowchart');
const panzoom = Panzoom(elem,{
    excludeClass: 'panzoom-exclude',canvas: true
});
const parent = elem.parentElement;
parent.addEventListener('wheel',panzoom.zoomWithWheel);

解决方法

我刚刚在研究完全相同的问题,并发现这是唯一的答案

Implementing pan and zoom in jsPlumb

使用的 PanZoom 看起来很旧 - 但想法是一样的,使用 JQuery Draggable 插件来处理可移动元素,而不是内置的 JsPlumb 插件。这允许元素移出边界。

下面的可拖动功能使用 PanZoom 库为我修复了它。

var that = this;
        var currentScale = 1;

        var element = $('.element');

        element.draggable({
            start: function (e) {
                //we need current scale factor to adjust coordinates of dragging element
                currentScale = that.panzoom.getScale();

                $(this).css("cursor","move");
                that.panzoom.setOptions({ disablePan: true });
            },drag: function (e,ui) {

                ui.position.left = ui.position.left / currentScale;
                ui.position.top = ui.position.top / currentScale;

                if ($(this).hasClass("jtk-connected")) {
                    that.jsPlumbInstance.repaintEverything();
                }
            },stop: function (e,ui) {
                var nodeId = $(this).attr('id');
                that.jsPlumbInstance.repaintEverything();
                $(this).css("cursor","");
                that.panzoom.setOptions({ disablePan: false });
            }
        });

我不确定在拖动时重绘所有内容是否那么有效 - 所以也许只重绘两个连接元素。