jsplumb设置锚点_记录一次基于jsPlumb流程图编辑器的开发过程
前言
接到项目需求后,发现没有做过相关项目,盘算着拖拽倒是没有问题,但是控件的连线好像挺复杂,所以先开始了一番搜索,希望有合适的轮子那最好不过了。看了这篇对比文章:超级好用的流程图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流程图编辑器的开发过程相关推荐
- jsplumb设置锚点_说明 · rysinal/jsPlumb Wiki · GitHub
jsPlumb 基本概念 一.默认属性 Anchor:锚点(连接点位置),可以设置在任何没有锚点的目标上(endPoint) Anchors:设置在connect的源和目标点的连接点位置,默认是 Bo ...
- jsplumb设置锚点_拓扑图编辑器-jsplumb基本配置
默认配置 jsPlumb的配置项有很多,如果不主动设置,jsPlumb就使用默认配置. 建议不要修改默认的配置,而是使用自定义的方式. Anchor:锚点,端点连接的位置 Anchors:多个锚点,[ ...
- jsplumb设置锚点_jsplumb 中文教程
https://wdd.js.org/jsplumb-chinese-tutorial/#/ 本文的图片是托管于七牛云的,由于使用的是测试域名,可能不知道哪天,图片就无法显示了.不过每个例子都有简单的 ...
- jsplumb设置锚点_JsPlumb初始化和添加连线、端点等
项目背景:项目上需要通过JsPlumb连线库表示两个表的关联关系,效果图如下: 左侧为数据仓库中的表分类,右侧上为模型设计区,下为数据预览区. 下面展示jsPlumb的初始化代码 jsPlumb.re ...
- jsplumb设置锚点_jsplumb
一.简介: jsplumb是jquery的一个插件,它能够让你用动态或静态的链接来连接html界面上的元素,并且从1.10版本开始,提供用鼠标拖动来连接.jsPlumb允许您使用SVG,Canvas或 ...
- jsplumb拖线_基于jsplumb插件制作可拖拽、保存流程图、重绘保存后的流程图总结...
1.重点参考博文 https://blog.csdn.net/j_bean/article/details/78092647 2.关键点总结 1)实现可视区域图形画满后,拖动整个画布的效果 a.最好不 ...
- mac mysql 设置短命令_短小强大的8个命令,分分钟帮你提高工作效率!
作为一名合格的Linux系统管理员,每天接触使用最多的莫过于Linux 命令了.很多人喜欢Linux,因为Linux 命令短小但却功能强大.在工作中经常使用的命令有很多,对于那些你未使用过的呢? 今天 ...
- Spring Cloud 设置Feign的日志记录级别
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-c ...
- python设置环境变量_小白Python进行中
一.安装 安装包的下载 在官网进行下载,我选用Python3.8.0. Welcome to Python.orgwww.python.org 安装 安装的时候可以借鉴该视频. Windows 10 ...
最新文章
- select选择框变得可以输入、编辑
- boost::container模块实现分配器参数的测试程序
- SimpleDateFormat与线程安全
- Taobao网的目录分类结构
- 高通sensor core培训笔记
- 2017第八届CSTQB国际软件测试高峰论坛圆满召开
- 用adb shell让APP获得Root权限 by SuperSU
- 参数校验@Valid
- Espresso测试示例
- FFmpeg创作GIF表情包教程来了!赶紧说声多谢乌蝇哥?
- Mysql.索引存储结构演进(面试一次搞定)
- 前端websocket和后端传输数据
- 高德地图发布2017上半年度公共交通报告
- web数据管理 期末
- iOS Run Loop
- 赋能数智化· 从数据到价值转变 | GCT冠骋信息高科技电子行业智能制造高端研讨会成功举办
- keycloak集群配置
- [渝粤教育] 中国地质大学 测试技术 复习题
- [置顶] 基于视频采集卡驱动的错误修改CX26828
- js 年 年-月 年-月-日 正则表达式