基于Vue.js的流程图框架
目前进度:
- 数据显示
- 节点拖拽
- 连线拖拽
- 多选,划框选择
- 多元素拖拽
- 节点创建:点击新增&预置节点拖拽新增
- 节点、连线编辑和删除
- 画板自适应
- 复制粘贴节点
- 快捷键处理
- 画板缩放
- 撤销重做
- 节点svg化
- 悬浮节点提示
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
# run unit tests
npm run unit
# run e2e tests
npm run e2e
# run all tests
npm test
...
分为两种组件,一种是主界面,一种是放置模板节点的这里叫模板界面,可以有多个模板界面。
对于模板界面,没有连线,且仅支持节点拖拽和鼠标悬浮事件
canvas画板由一个 model 对象控制数据,其中包含两种元素,即节点和连线
{
nodes: [Node],
edges: [Edge]
}
元素的操作,除了在各种组件上的处理外,事件还会派发到canvas上,由canvas决定是否推入历史栈
一些功能按钮,在外部通过slot插槽传入canvas组件,样式让用户自定义
主要元素之一
{
id: Number | String, //唯一标识符
name: String,//节点名称
x: Number, // 节点相对canvas的x坐标
y: Number, // 节点相对canvas的y坐标
connectors: [Connector],// 连接点
readonly: Boolean=false,// 是否只读,只读模式下仅支持鼠标悬浮事件
addition:Object// 支持拓展
}
一个node最多仅有两个connector(左右两侧两种类型)
用于连线时使用
{
// 连接点类型,取值范围:leftConnector/rightConnector
[type: String]: {
id: Number | String, //唯一标识符
}
}
组件配置:
主要元素之一
由某节点的rightConnector连接点和另一节点的leftConnector连接点相连,带箭头
{
source: Connector.id,
destination: Connector.id,
active: Boolean=false,// 应用连线流动动画
label:String//连线的标签
}
处于连线中心
event.clientX: 基于网页左上角的x坐标
getBoundingClientRect: 相对视口的坐标(相对),而不是网页左上角(绝对),比如有滚动条且向下滚动,获得的top值偏小
连线随节点变动而变动
如何做到高效实时拖拽?
handleDragstart () {
console.log('node Dragstart:', event)
let elementBox = this.$el.getBoundingClientRect()
this.eventPointOffset.x = event.clientX - elementBox.left
this.eventPointOffset.y = event.clientY - elementBox.top
// this.nodedraggingservice.dragstart(event)
let dataTransfer = event.dataTransfer
dataTransfer.dropEffect = 'move'
dataTransfer.setData('Text', event.target.id)
dataTransfer.setDragImage(this.$el, this.eventPointOffset.x, this.eventPointOffset.y)
this.$emit('node-dragstart', this.node)
this.updateConnectorPosition()
},
handleDragging (event) {
console.log('handleDragging')
if (!(event.clientX && event.clientY)) {
return
}
let newNode = Object.assign(this.node, {
x: event.clientX - this.canvas.left - this.eventPointOffset.x,
y: event.clientY - this.canvas.top - this.eventPointOffset.y
})
this.updateNode({
node: this.node,
newNode
})
this.updateConnectorPosition()
},
handleDragend () {
console.log('node Dragend:', event)
let newNode = Object.assign(this.node, {
x: event.clientX - this.canvas.left - this.eventPointOffset.x,
y: event.clientY - this.canvas.top - this.eventPointOffset.y
})
this.updateNode({
node: this.node,
newNode,
isPushState: true
})
this.$emit('node-dragend', event)
// this.updateConnectorPosition()
}
firefox 下 drag,dragend拿到的event.clientX/Y 为0,只能从容器的 dragover 和drop事件的event中获取
firefox 必须使用dataTransfer.setData('text', xxx)
ie11上会报错且getData拿不到数据?
dragover时拿不到dataTransfer.getData
这样做的好处在于,被拖动元素将不可拖动至任意地方,仅在容器中重绘
chrome 中 dragover 不能拿到event.dataTransfer.getData('Text')
从测试上来看,mouse兼容性好,且性能更好一点
类似 mouseover,它们两者之间的差别是 mouseenter 不会冒泡(bubble)