前言

接到项目需求后,发现没有做过相关项目,盘算着拖拽倒是没有问题,但是控件的连线好像挺复杂,所以先开始了一番搜索,希望有合适的轮子那最好不过了。看了这篇对比文章:超级好用的流程图js框架,也看了一个新出的轮子topology,选轮子的时候我习惯性去npm trends,找一些类似的轮子横向对比看看各项数据,然后再去对应的仓库找相关文档,做几个demo先试试能满足需求好上手不。

最后选择了jsPlumb,选择原因:1.开源。2.官网中的样例基本可以满足我的项目需求。3.作者一直有维护,文档还算详细,易懂。

编辑器结构设计

编辑页面分为左中右三部分,左侧控件部分,中间画板,右侧为流程节点配置编辑区域。左侧控件可以拖拽到中间画板,形成流程中的某个节点,可以进行节点连接。点击或移动节点,右侧编辑页面会打开,可以对节点进行配置编辑。每种类型的节点都对应一个编辑栏的组件,而每一个不同id的节点都有一个配置项数据。

控件拖拽入画板

使用HTML 拖放 API,主要使用了:

dragstart:当用户开始拖动一个元素或选中的文本时触发(见开始拖动操作)

dragover:当元素或选中的文本被拖到一个可释放目标上时触发(每100毫秒触发一次)

drop:当元素或选中的文本在可释放目标上被释放时触发(见执行释放)

使用示例:

  • v-for="menu in menus"

    :key="menu.type"

    draggable

    @dragstart="dragstart($event, menu)"

    >

复制代码methods: {

dragstart(event, menu) {

event.dataTransfer.setData('text/plain', JSON.stringify(menu))

},

drop(event) {

let data = event.dataTransfer.getData('text/plain')

let dom = document.querySelector('.drag-content')

let left = event.x - dom.offsetLeft + dom.scrollLeft

let top = event.y - dom.offsetTop + dom.scrollTop

data = JSON.parse(data)

left = left - left % 4 + 'px' // 网格grid: [4, 4], 便于直线对齐(优化)

top = top - top % 4 + 'px'

data.id = uuidv1()

data.position = { left, top }

this.flowList.push(data)

}

}

复制代码使用drag event 的 dataTransfer属性保存自定义的控件数据,进行传递。另外也可以定义一个currentMenu保存数据,但这需要重置。

画布中的元素是相对画布定位的,所以在drog事件中需要对坐标进行处理。

在dragover事件中event.preventDefault()很关键。

节点拖拽连接

流程图画板的操作,主要是基于jsPlumb。

官方文档 jsplumb.github.io/jsplumb/hom…

1.jsPlumb默认是在浏览器的窗口上注册的,为整个页面提供一个静态实例。可以使用getInstance方法来实例化jsPlumb的独立实例。并设置默认容器,限制在容器内拖拽。

mounted() {

this.instance = jsPlumb.getInstance()

this.instance.setContainer('containerId')

this.instance.ready(() => {

// 绘制,如连接锚点

})

}

复制代码

2.设置节点可拖拽,instance.draggable

arrays

jsPlumbInstance.draggable(["elementOne", "elementTwo"]);

jQuery selectors

jsPlumbInstance.draggable($(".someClass"));

NodeLists

var els = document.querySelectorAll(".someClass")

jsPlumbInstance.draggable(els)

使用示例:

this.$nextTick(() => {

var els = document.querySelectorAll('.drag-menu')

this.instance.draggable(els, {

containment: true, // 设置父元素为默认容器, 与上面setContainer作用一样。

grid: [4, 4] // 要将元素约束到网格中,移动的时候不再平滑,但是便于节点对齐。

})

})

复制代码

3.设置锚点,instance.addEndpoint

jsPlumb源码中显示addEndpoint方法接受三个参数,节点元素的id,后面的是配置参数会合并。

// --------------------------- jsPlumbInstance public API -----------------------------

this.addEndpoint = function (el, params, referenceParams) {

referenceParams = referenceParams || {};

var p = jsPlumb.extend({}, referenceParams);

jsPlumb.extend(p, params);

p.endpoint = p.endpoint || _currentInstance.Defaults.Endpoint;

p.paintStyle = p.paintStyle || _currentInstance.Defaults.EndpointStyle;

...

复制代码

使用示例:

let uuid = uuidv1()

this.instance.addEndpoint(elementId, {

anchor: [ 0.5, 0, 0, -1, 0, 0, 'anchor-name'],

uuid,

isSource: true,

isTarget: true,

paintStyle: { fill: '#D8DDE6', radius: 5 },

connector: 'Flowchart',

connectorStyle: { stroke: '#D8DDE6', strokeWidth: 2 },

connectorOverlays: [

[

'Arrow',

{

id: uuid // 这里是为了监听到连接事件后,传递锚点uuid

location: 0.5,

width: 8,

height: 5,

paintStyle: { stroke: '#D8DDE6', fill: '#D8DDE6' }

}

],

['Label', { label: 'xxxx', location: 0.25 }]

]

});

复制代码

重点参数说明:

anchor:锚点位置有四种类型

Static:静态,会固定到元素上的某个点,不会移动

Dynamic:动态,是静态锚的集合,就是jsPlumb每次连接时选择最合适的锚

Perimeter anchors:周边锚,动态锚的应用

Continuous anchors:连续锚

这里着重解释下使用基于数组的形式来定义锚点位置:[x,y,dx,dy,offsetX,offsetY]

x表示锚点在横轴上的距离,y表示锚点在纵轴上的距离,这两个值可以从0到1来设置,0.5为center

dx表示锚点向横轴射出线,dy表示锚点向纵轴射出线,有0,-1,1三个值来设置。0为不放射线(控制从锚点射出的连接线的初始方向)

offsetX表示锚点横轴偏移量x(px),offsetY表示锚点纵轴偏移量y(px)

connectorOverlays:连接线样式设置

Arrow--一个可配置的箭头,沿着连接器的某个点涂上。你可以控制箭头的长度和宽度,"折返 "点--尾部折返的点,以及方向(允许的值是1和-1;1是默认值,意味着指向连接的方向)

Label - 可配置的标签,沿着连接器的某个点涂上标签。

PlainArrow - 箭头形状为三角形的箭头,没有反折。

Diamond - 菱形。

Custom - 允许你自己创建 Overlay - 你的 Overlay 可以是你喜欢的任何 DOM 元素。

PlainArrow和Diamond实际上只是一般Arrow覆盖层的配置实例)。

其中可以对同一个节点有多个锚点,各个锚点分别设置不同的label,区分不同的分支。这是两个锚点直接的桥梁,关联节点关系的时候使用。而分支名的字典表和其代表的含义,可以在右侧编辑栏中配置。

connector: 连接线svg形状

Straight: 直线

Bezier: 贝塞尔曲线

Flowchart: 具有90度转折点的流程线

StateMachine: 状态机

3.连接锚点,instance.connect

可以手动拖动锚点直接连接到另外一个节点的锚点,其中要区分(isSource和isTarget),添加锚点的时候设置了此锚点是出发点还是终点。

可以使用instance.connect连接两个锚点。

我在创建锚点的时候,在配置参数里面添加uuid,然后通过uuid连接

this.instance.addEndpoint("elId1", { uuid:"abcdefg" }, endpointOptions )

this.instance.addEndpoint("elId2", { uuid:"hijklmn" }, endpointOptions )

this.instance.connect({uuids:["abcdefg", "hijklmn"]})

复制代码

3.删除锚点

删除单一锚点var ep = this.instance.addEndpoint(elId, { ... })

...

this.instance.deleteEndpoint(ep)

复制代码

删除所有锚点

同时也删除每个连接,这个方法和jsPlumb.reset很相似,只是这个方法不删除已注册的事件处理程序this.instance.deleteEveryEndpoint()

复制代码

4.建立节点关系

监听锚点连接事件,其中targetId和sourceId我们可以获取节点id,另外connectorOverlays[0][1].id就是对于锚点的id,这里记录锚点id,维护一份this.connectIds是为了再次编辑的时候,能通过instance.connect({uuids})直接连接节点。数据格式:this.flowList = [{

type: '', // 节点类型

id: '', // uuid

parents: [], // 父节点id

children: [], // 子节点id

points: [], // 自身锚点信息

config: {}, // 节点配置信息

branch: {} // 分支信息,就是连线上的label信息

}]

this.instance.bind('connection', info => {

let source, target

for (let item of this.flowList) {

if (source && target) break

if (!source && item.id === info.sourceId) {

source = item

} else if (!target && item.id === info.targetId) {

target = item

}

}

// source和target一定能find到

if (!source.children.includes(info.targetId)) {

source.children.push(info.targetId)

}

if (!target.parents.includes(info.sourceId)) {

target.parents.push(info.sourceId)

}

if (info.sourceEndpoint.connectorOverlays[1]) {

target.branch = info.sourceEndpoint.connectorOverlays[1][1].label

// 我将分支信息带在子节点上

}

let sid = info.sourceEndpoint.connectorOverlays[0][1].id

let tid = info.targetEndpoint.connectorOverlays[0][1].id

// 点击某个锚点,未断开连接就直接连接新的锚点,需要删除之前的链接

this.connectIds = this.connectIds.filter(el => el[0] !== sid && el[1] !== tid)

this.connectIds.push([sid, tid])

// 这些connectIds是预览流程图的时候直接连接锚点使用

})

复制代码

监听断开连接事件,将存储的锚点关系列表进行数据更新。this.instance.bind('connectionDetached', info => {

// 同上bind('connection'),find到source、target

source.children = source.children.filter(id => id !== info.targetId)

target.parents = target.parents.filter(id => id !== info.sourceId)

let sid = info.sourceEndpoint.connectorOverlays[0][1].id

let tid = info.targetEndpoint.connectorOverlays[0][1].id

this.connectIds = this.connectIds.filter(el => !_.isEqual(el, [sid, tid]))

})

复制代码

总结

整个编辑画板中的节点元素、锚点、连线都是兄弟元素,都是相对于父节点定位,在节点附近自定义添加其他元素也比较容易,其中连线是采用svg绘制,其中正交连线我觉得是有一定难度的。项目中的元素分支后续可以优化,目前是在确定锚点的时候确定了此分支的名称,当从锚点拽出分支线的时候,其中的label标签才会告诉用户这是什么分支。这一步可以提前,在生成锚点的时候自动生成连接线并展示出分支名。

这是近期项目的自我记录,不方便放UI图和整体源码,如果有做流程图的同行需要交流可私聊。文章最后我会贴上学习jsPlumb的相关文章 :

官方文档: jsplumb.github.io/jsplumb/hom…

前辈总结的中文教程: wdd.js.org/jsplumb-chi…

jsPlumb.jsAPI阅读笔记: www.cnblogs.com/leomyili/p/…

流程图-正交连线的算法的一种简单实现: juejin.im/post/5b7382…

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[记录一次基于jsPlumb流程图编辑器的开发过程]http://www.zyiz.net/tech/detail-133315.html

jsplumb设置锚点_记录一次基于jsPlumb流程图编辑器的开发过程相关推荐

  1. jsplumb设置锚点_说明 · rysinal/jsPlumb Wiki · GitHub

    jsPlumb 基本概念 一.默认属性 Anchor:锚点(连接点位置),可以设置在任何没有锚点的目标上(endPoint) Anchors:设置在connect的源和目标点的连接点位置,默认是 Bo ...

  2. jsplumb设置锚点_拓扑图编辑器-jsplumb基本配置

    默认配置 jsPlumb的配置项有很多,如果不主动设置,jsPlumb就使用默认配置. 建议不要修改默认的配置,而是使用自定义的方式. Anchor:锚点,端点连接的位置 Anchors:多个锚点,[ ...

  3. jsplumb设置锚点_jsplumb 中文教程

    https://wdd.js.org/jsplumb-chinese-tutorial/#/ 本文的图片是托管于七牛云的,由于使用的是测试域名,可能不知道哪天,图片就无法显示了.不过每个例子都有简单的 ...

  4. jsplumb设置锚点_JsPlumb初始化和添加连线、端点等

    项目背景:项目上需要通过JsPlumb连线库表示两个表的关联关系,效果图如下: 左侧为数据仓库中的表分类,右侧上为模型设计区,下为数据预览区. 下面展示jsPlumb的初始化代码 jsPlumb.re ...

  5. jsplumb设置锚点_jsplumb

    一.简介: jsplumb是jquery的一个插件,它能够让你用动态或静态的链接来连接html界面上的元素,并且从1.10版本开始,提供用鼠标拖动来连接.jsPlumb允许您使用SVG,Canvas或 ...

  6. jsplumb拖线_基于jsplumb插件制作可拖拽、保存流程图、重绘保存后的流程图总结...

    1.重点参考博文 https://blog.csdn.net/j_bean/article/details/78092647 2.关键点总结 1)实现可视区域图形画满后,拖动整个画布的效果 a.最好不 ...

  7. mac mysql 设置短命令_短小强大的8个命令,分分钟帮你提高工作效率!

    作为一名合格的Linux系统管理员,每天接触使用最多的莫过于Linux 命令了.很多人喜欢Linux,因为Linux 命令短小但却功能强大.在工作中经常使用的命令有很多,对于那些你未使用过的呢? 今天 ...

  8. Spring Cloud 设置Feign的日志记录级别

    <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-c ...

  9. python设置环境变量_小白Python进行中

    一.安装 安装包的下载 在官网进行下载,我选用Python3.8.0. Welcome to Python.org​www.python.org 安装 安装的时候可以借鉴该视频. Windows 10 ...

最新文章

  1. select选择框变得可以输入、编辑
  2. boost::container模块实现分配器参数的测试程序
  3. SimpleDateFormat与线程安全
  4. Taobao网的目录分类结构
  5. 高通sensor core培训笔记
  6. 2017第八届CSTQB国际软件测试高峰论坛圆满召开
  7. 用adb shell让APP获得Root权限 by SuperSU
  8. 参数校验@Valid
  9. Espresso测试示例
  10. FFmpeg创作GIF表情包教程来了!赶紧说声多谢乌蝇哥?
  11. Mysql.索引存储结构演进(面试一次搞定)
  12. 前端websocket和后端传输数据
  13. 高德地图发布2017上半年度公共交通报告
  14. web数据管理 期末
  15. iOS Run Loop
  16. 赋能数智化· 从数据到价值转变 | GCT冠骋信息高科技电子行业智能制造高端研讨会成功举办
  17. keycloak集群配置
  18. [渝粤教育] 中国地质大学 测试技术 复习题
  19. [置顶] 基于视频采集卡驱动的错误修改CX26828
  20. js 年 年-月 年-月-日 正则表达式

热门文章

  1. 支付宝是如何在年账单里坑你的?
  2. winform控件之ListView
  3. Springboot毕设项目健康食谱推荐分享系统h5376(java+VUE+Mybatis+Maven+Mysql)
  4. Java中如何导入DW当中_用Dreamweaver插入Java特效方法
  5. 朱近之:关于云计算的十大误区
  6. vue中 el-input手机号码或座机号码做验证
  7. 零基础怎么快速学习verilog语言
  8. 音视频:PCM转AWV
  9. js中offsetParent详解
  10. VNCTF 2023 部分wp