当前位置:   article > 正文

jsPlumb初认识

jsplumb

hello,大家好,我是前端小老弟儿,最近老弟儿接到这样一个需求,实现一个流程图,可以连线,右键操作,以及删除连线等,如下所示得流程图。使用得插件是: jsPlumb;所以就简单的介绍一下jsPlumb基本使用
在这里插入图片描述

什么是jsplumb

jsPlumb是一个强大得JavaScript连线库,提供html元素的拖放、连线等功能,可绘制不同类型、样式的连线,适用于开发web页面的图表、建模工具等。同时也支持vue2.0 ,react和Angular 。

jsplumb 能干什么?

该框架适用于必须回值图表得web应用程序,比如流程图设计,组织架构图设计等。

jsplumb的基本使用:

  1. 配置jsPlumb
npm install jsplumb
  • 1
  1. 引入
import { jsPlumb } from "jsplumb";
  • 1

jsplumb的基本方法:

  1. ready() 确保jsPlumb 插件已经开始渲染
jsPlumb.ready(function() {
    ...
    // your jsPlumb related init code goes here
    ...
});
  • 1
  • 2
  • 3
  • 4
  • 5
  1. batch() 绘制节点以及节点相关信息
jsPlumb.batch(function() {
  // import here
  for (var i = 0, j = connections.length; i < j; i++) {
      jsPlumb.connect(connections[i]);
  }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. bind() jsPlumb 节点渲染完成后,在这里可以添加事件,在初始化时,直接添加事件
 // 连线事件
 jsPlumb.bind("connection", (info, event) => {
        this.bindLinkEvent(info.connection);
        this.data.links.push([info.sourceId, info.targetId]);
      });
  • 1
  • 2
  • 3
  • 4
  • 5
  1. getInstance()
    jsPlumb默认注册在浏览器的window对象中,为整个页面提供了一个静态实例(jsPlumb)可以直接使用,当然你也可以使用getInstance方法来单独创建一个实例。即:
this.flowInst = jsPlumb.getInstance();
  • 1

同时,getInstance方法接受一个参数,可以更改实例的配置

this.flowInst = jsPlumb.getInstance({
    Connector : [ "Bezier", { curviness: 150 } ],
    Anchors : [ "TopCenter", "BottomCenter" ],
    ...
});
  • 1
  • 2
  • 3
  • 4
  • 5
  1. jsPlumb.connect(…) 用于创建连线
this.flowInst.connect({
        source: 'item_left', // 源
        target: 'item_right', // 目标
        endpoint: 'Dot' // 线的类型
      })
  • 1
  • 2
  • 3
  • 4
  • 5
  1. jsPlumb.addEndpoint(…) 用来增加端点
// id: 增加端点得id
// common:端点的配置信息
this.flowInst.addEndpoint(id,{common})
this.flowInst.addEndpoint('item_left', {
        anchors: ['Right']
      })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. jsPlumb.draggable() 节点是否可拖拽
let common = { 
        containment?: string
        start?: (params:DragEventCallbackOptions) => void
        drag?: (params:DragEventCallbackOptions) => void
        stop?: (params:DragEventCallbackOptions) => void
        cursor?: string
        zIndex?: number
}
this.flowInst.draggable(node._id, {common}); // 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

此方法有两个参数:
第一个参数为可拖拽节点的id,
第二个参数为函数对象,有6个参数,start,drag,stop 三个函数中可以获取元素节点位置等。

基本配置

{
    Anchor: "BottomCenter", //锚点位置,如left,top,bottom等;对任何没有声明描点的Endpoint设置锚点,用于source及诶单或target节点
    Anchors: [ null, null ], //连线的source和target Anchor
    ConnectionsDetachable: true, //连线是否可用鼠标分离
    ConnectionOverlays: [  //连线的叠加组件,如箭头、标签
        ["Arrow", {  //箭头参数设置
            location: 1,
            visible:true,
            width:11,
            length:11,
            id:"ARROW",
            events:{
                click:function() { }
            }
        } ],
        [ "Label", {  //标签参数设置
            location: 0.1,
            id: "label",
            cssClass: "aLabel", //hover时label的样式名
            events:{
                tap:function() { }
            },
            visible: true
        }]
    ],
    Connector: "Bezier", //连线的类型,流程图(Flowchart)、贝塞尔曲线等
    Container: null, //父级元素id;假如页面元素所在上层不同,最外层父级一定要设置
    DoNotThrowErrors: false, //如果请求不存在的Anchor、Endpoint或Connector,是否抛异常
    DragOptions: {cursor: 'pointer', zIndex: 2000}, //通过jsPlumb.draggable拖拽元素时的默认参数设置
    DropOptions: { }, //target Endpoint放置时的默认参数设置
    Endpoint: "Dot", //端点(锚点)的样式声明
    Endpoints: [ null, null ], //用jsPlumb.connect创建连接时,source端点和target端点的样式设置
    EndpointOverlays: [ ], //端点的叠加物
    EndpointStyle: { fill : "#456" }, //端点的默认样式
    EndpointStyles: [ null, null ], //连线的source和target端点的样式
    EndpointHoverStyle: { fill: "#ec9f2e" }, //端点hover时的样式
    EndpointHoverStyles: [ null, null ], //连线的source和target端点hover时的样式
    HoverPaintStyle: {stroke: "#ec9f2e" }, //连线hover时的样式
    LabelStyle: { color: "black" }, //标签的默认样式,用css写法。
    LogEnabled: false, //是否开启jsPlumb内部日志
    Overlays: [ ], //连线和端点的叠加物
    MaxConnections: 1, //端点支持的最大连接数
    PaintStyle: { lineWidth : 8, stroke : "#456" }, //连线样式
    ReattachConnections: false, //是否重新连接使用鼠标分离的线?
    RenderMode: "svg", //默认渲染模式
    Scope: "jsPlumb_DefaultScope" //范围,具有相同scope的点才可连接?
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

具体实现方案
data示例

data: {
        links: [
          [
            "46fda179-5947-4fe7-90aa-588a61b85694",
            "ecebf461-4532-4840-8672-017857686335",
          ],
          [
            "ecebf461-4532-4840-8672-017857686335",
            "245057e5-a747-49e0-9e66-9fb537d351a3",
          ],
          [
            "245057e5-a747-49e0-9e66-9fb537d351a3",
            "690c46fb-c8d9-4c7f-af35-89c30deb9b98",
          ],
        ],
        nodes: [
          {
            _id: "46fda179-5947-4fe7-90aa-588a61b85694",
            name: "START",
            description: "START",
            descStr: "",
            class: "",
            pos: [313.33333333333337, 160],
            endpoints: [{ isSource: true, maxConnections: -1 }],
            nodeType: "START",
            contextMenus: [
              { icon: "el-icon-delete", text: "删除", event: "delete" },
            ],
          },
          {
            _id: "ecebf461-4532-4840-8672-017857686335",
            name: "ONE",
            description: "ONE",
            descStr: "ONE",
            class: "",
            pos: [325, 314],
            endpoints: [{}, {}],
            nodeType: "ONE",
            contextMenus: [
              { icon: "el-icon-delete", text: "删除", event: "delete" },
              { icon: "el-icon-edit", text: "编辑", event: "edit" },
              { icon: "el-icon-edit", text: "修改描述", event: "desc" },
              { icon: "fa fa-clone", text: "克隆", event: "copy" },
            ],
          },
          {
            _id: "245057e5-a747-49e0-9e66-9fb537d351a3",
            name: "TWO",
            description: "TWO",
            descStr: "TWO",
            class: "",
            pos: [328.33333333333337, 456.6666666666667],
            endpoints: [{}, {}],
            nodeType: "TWO",
            contextMenus: [
              { icon: "el-icon-delete", text: "删除", event: "delete" },
              { icon: "el-icon-edit", text: "编辑", event: "edit" },
              { icon: "el-icon-edit", text: "修改描述", event: "desc" },
              { icon: "fa fa-clone", text: "克隆", event: "copy" },
            ],
          },
          {
            _id: "690c46fb-c8d9-4c7f-af35-89c30deb9b98",
            name: "END",
            description: "END",
            descStr: "",
            class: "",
            pos: [355, 650],
            endpoints: [{ isSource: false, isTarget: true, maxConnections: 1 }],
            nodeType: "END",
            contextMenus: [
              { icon: "el-icon-delete", text: "删除", event: "delete" },
            ],
          },
        ],
        props: {
          position: [-34, -49],
        },
      },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

mounted中:

mounted() {
    this.container = this.$el.querySelector(".workflow-container");
    jsPlumb.ready(() => {
      this.createFlow();
    });
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

methods:

createFlow() {
      console.log('绘制工作流')
      this.flowInst = jsPlumb.getInstance(this.defaultOption);
      this.flowInst.setContainer(this.container);
      // suspend drawing and initialise.
      this.flowInst.batch(() => {
        this.drawNodes();
        for (const link of this.data.links) {
          const conn = this.flowInst.connect({
            uuids: [link[0] + "Bottom", link[1] + "Top"],
          });
          //绑定右键事件
          this.bindLinkEvent(conn);
        }
      });
      //建立连接事件,更新到data 连线事件
      this.flowInst.bind("connection", (info, event) => {
        this.bindLinkEvent(info.connection);
        this.data.links.push([info.sourceId, info.targetId]);
      });
      //删除连接事件,更新到data
      this.flowInst.bind("connectionDetached", (info, event) => {
        for (let [k, o] of this.data.links.entries()) {
          if (o[0] === info.sourceId && o[1] === info.targetId) {
            this.data.links.splice(k, 1);
            break;
          }
        }
      });
      // //更改连接事件,更新到data。更改连接时也会触发connection事件
      this.flowInst.bind("connectionMoved", (info, event) => {
        for (let [k, o] of this.data.links.entries()) {
          if (
            o[0] === info.originalSourceId &&
            o[1] === info.originalTargetId
          ) {
            this.data.links.splice(k, 1);
            break;
          }
        }
      });
      this.flowInst.setZoom(this.zoom);
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

绘制元素

drawNodes() {
      let vm = this;
      for (let node of this.data.nodes) {
        let str = `<div id="${node._id}" class="point node-canselect">
                        <div class="title">${node.name}</div>
                        <div class="desc">${node.nodeType}</div>`;
        //任务运维中,针对可优化的节点,展示'可优化'的logo
          str = str + "</div>";
        let dom = $(`${str}`);
        // node.pos[0]node.pos[1]
        dom.css({
          left: node.pos[0] + "px",
          top: node.pos[1] + "px",
        });
        //绑定右键事件
        if (!_.isEmpty(node.contextMenus)) {
          dom.contextmenu((event) => {
            this.nodeMenu.pos = this.getContextMenuPos(event);
            this.nodeMenu.obj = node;
            this.nodeMenu.visible = true;
            this.connMenu.visible = false;
            this.rightButtonVisible = false;
            return false;
          });
        }
        //绑定双击事件
        dom.dblclick(() => {
          this.$emit("nodeDblclickEvent", node);
        });
        this.container.append(dom.get(0));

        if (_.isArray(node.endpoints)) {
          for (let [index, endpoint] of node.endpoints.entries()) {
            let p = {
              connectorStyle: {
                strokeWidth: 4,
                stroke: "#61B7CF",
                joinstyle: "round",
              },
              dragAllowedWhenFull: false,
              isTarget: index === 0,
              isSource: index === 1,
              ...endpoint,
            };
            p.anchor = p.isSource ? "Bottom" : p.isTarget ? "Top" : "";
            p.uuid = node._id + p.anchor;
            this.flowInst.addEndpoint(node._id, p);
          }
        }
        if (!this.option.readonly) {
          this.flowInst.draggable(node._id, {
            stop: function(event) {
              vm.nodeDrop(event.el, event.pos);
            },
          });
        }
      }
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

最后实现的功能和样式如下:
实现删除,连线功能

在这里插入图片描述
可以任意拖拽
在这里插入图片描述

参考文献:
jsplumb 中文基础教程: https://wdd.js.org/jsplumb-chinese-tutorial/#/?id=_1-jsplumb-%e4%b8%ad%e6%96%87%e5%9f%ba%e7%a1%80%e6%95%99%e7%a8%8b

jsPlumb Toolkit Documentation: https://docs.jsplumbtoolkit.com/toolkit/current/index.html

JsPlumb.js使用总结: https://nanastef.github.io/jsPlumb/

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/117141
推荐阅读
相关标签
  

闽ICP备14008679号