事件系统
视图交互事件
通过鼠标、键盘或者各种可交互的组件与应用产生交互时触发的事件,如单击节点 'node:click'
等。
鼠标事件
事件 | cell 节点/边 | node 节点 | edge 边 | blank 画布空白区域 |
---|---|---|---|---|
单击 | cell:click | node:click | edge:click | blank:click |
双击 | cell:dblclick | node:dblclick | edge:dblclick | blank:dblclick |
右键 | cell:contextmenu | node:contextmenu | edge:contextmenu | blank:contextmenu |
鼠标按下 | cell:mousedown | node:mousedown | edge:mousedown | blank:mousedown |
移动鼠标 | cell:mousemove | node:mousemove | edge:mousemove | blank:mousemove |
鼠标抬起 | cell:mouseup | node:mouseup | edge:mouseup | blank:mouseup |
鼠标滚轮 | cell:mousewheel | node:mousewheel | edge:mousewheel | blank:mousewheel |
鼠标进入 | cell:mouseenter | node:mouseenter | edge:mouseenter | graph:mouseenter |
鼠标离开 | cell:mouseleave | node:mouseleave | edge:mouseleave | graph:mouseleave |
需要注意的是,这里的 mousemove
事件和通常的鼠标移动事件有所区别,它需要在鼠标按下后移动鼠标才能触发。
除了 mouseenter
和 mouseleave
外,事件回调函数的参数都包含鼠标相对于画布的位置 x
、y
和鼠标事件对象 e
等参数。
graph.on('cell:click', ({ e, x, y, cell, view }) => { })
graph.on('node:click', ({ e, x, y, node, view }) => { })
graph.on('edge:click', ({ e, x, y, edge, view }) => { })
graph.on('blank:click', ({ e, x, y }) => { })
graph.on('cell:mouseenter', ({ e, cell, view }) => { })
graph.on('node:mouseenter', ({ e, node, view }) => { })
graph.on('edge:mouseenter', ({ e, edge, view }) => { })
graph.on('graph:mouseenter', ({ e }) => { })
点击下面 Demo 中的画布和节点。
自定义点击事件
我们可以在节点/边的 DOM 元素上添加自定义属性 event
或 data-event
来监听该元素的点击事件,例如:
node.attr({
// 表示一个删除按钮,点击时删除该节点
image: {
event: 'node:delete',
xlinkHref: 'trash.png',
width: 20,
height: 20,
},
})
可以通过绑定的事件名 node:delete
或通用的 cell:customevent
、node:customevent
、edge:customevent
事件名来监听。
graph.on('node:delete', ({ view, e }) => {
e.stopPropagation()
view.cell.remove()
})
graph.on('node:customevent', ({ name, view, e }) => {
if (name === 'node:delete') {
e.stopPropagation()
view.cell.remove()
}
})
画布缩放/平移
事件名 | 回调参数 | 说明 |
---|---|---|
scale | { sx: number; sy: number; ox: number; oy: number } | 缩放画布时触发,sx 和 sy 是缩放比例,ox 和 oy 是缩放中心。 |
resize | { width: number; height: number } | 改变画布大小时触发,width 和 height 是画布大小。 |
translate | { tx: number; ty: number } | 平移画布时触发,tx 和 ty 分别是 X 和 Y 轴的偏移量。 |
graph.on('scale', ({ sx, sy, ox, oy }) => { })
graph.on('resize', ({ width, height }) => { })
graph.on('translate', ({ tx, ty }) => { })
节点或边缩放/平移/旋转
事件名 | 回调参数 | 说明 |
---|---|---|
node:move | { e: JQuery.MouseDownEvent; x: number; y: number; node: Node; view: NodeView } | 开始移动节点时触发。 |
node:moving | { e: JQuery.MouseMoveEvent; x: number; y: number; node: Node; view: NodeView } | 移动节点时触发。 |
node:moved | { e: JQuery.MouseUpEvent; x: number; y: number; node: Node; view: NodeView } | 移动节点后触发。 |
edge:move | { e: JQuery.MouseDownEvent; x: number; y: number; node: Node; view: NodeView } | 开始移动边时触发。 |
edge:moving | { e: JQuery.MouseMoveEvent; x: number; y: number; node: Node; view: NodeView } | 移动边时触发。 |
edge:moved | { e: JQuery.MouseUpEvent; x: number; y: number; node: Node; view: NodeView } | 移动边后触发。 |
node:resize | { e: JQuery.MouseDownEvent; x: number; y: number; node: Node; view: NodeView } | 开始调整节点大小时触发。 |
node:resizing | { e: JQuery.MouseMoveEvent; x: number; y: number; node: Node; view: NodeView } | 调整节点大小时触发。 |
node:resized | { e: JQuery.MouseUpEvent; x: number; y: number; node: Node; view: NodeView } | 调整节点大小后触发。 |
node:rotate | { e: JQuery.MouseDownEvent; x: number; y: number; node: Node; view: NodeView } | 开始旋转节点时触发。 |
node:rotating | { e: JQuery.MouseMoveEvent; x: number; y: number; node: Node; view: NodeView } | 旋转节点时触发。 |
node:rotated | { e: JQuery.MouseUpEvent; x: number; y: number; node: Node; view: NodeView } | 旋转节点后触发。 |
参数中的 x
和 y
是鼠标相对于画布的坐标。
graph.on('node:moved', ({ e, x, y, node, view }) => { })
graph.on('node:resized', ({ e, x, y, node, view }) => { })
graph.on('node:rotated', ({ e, x, y, node, view }) => { })
节点嵌入
事件名 | 回调参数 | 说明 |
---|---|---|
node:embed | { e: JQuery.MouseDownEvent; x: number; y: number; node: Node; view: NodeView, currentParent: Node } | 开启嵌入,在开始拖动节点时触发。 |
node:embedding | { e: JQuery.MouseMoveEvent; x: number; y: number; node: Node; view: NodeView, currentParent: Node, candidateParent: Node } | 寻找目标节点过程中触发。 |
node:embedded | { e: JQuery.MouseUpEvent; x: number; y: number; node: Node; view: NodeView, previousParent: Node, currentParent: Node } | 完成节点嵌入后触发。 |
边连接/取消连接
当拖动边的起始/终止箭头将边连接到节点/边或者将边从节点/边上分离后触发 edge:connected
,回调函数的参数如下。
interface Args {
e: JQuery.MouseUpEvent // 鼠标事件对象
edge: Edge // 边
view: EdgeView // 边的视图
isNew: boolean // 是否是新创建的边
type: Edge.TerminalType // 操作的是起始箭头还是终止箭头('source' | 'target')
previousCell?: Cell | null // 交互前连接到的节点/边
previousView?: CellView | null // 交互前连接到的节点/边的视图
previousPort?: string | null // 交互前连接到的链接桩 ID
previousPoint?: Point.PointLike | null // 交互前连接到的点(将边的终端从空白处拖动到节点/边上时记录起始终端的位置)
previousMagnet?: Element | null // 交互前连接到的元素
currentCell?: Cell | null // 交互后连接到的节点/边
currentView?: CellView | null // 交互后连接到的节点/边的视图
currentPort?: string | null // 交互后连接到的链接桩 ID
currentPoint?: Point.PointLike | null // 交互后连接到的点(将边的终端从节点/边上拖动到空白处时记录拖动后终端的位置)
currentMagnet?: Element | null // 交互后连接到的元素
}
我们可以通过 isNew
来判断连线完成后,对应的边是否是新创建的边。比如从一个链接桩开始,创建了一条边并连接到另一个节点/链接桩,此时 isNew
就为 true
。
graph.on('edge:connected', ({ isNew, edge }) => {
if (isNew) {
// 对新创建的边进行插入数据库等持久化操作
}
})
特别注意的是,参数中的 previous...
是记录操作终端在连接/取消连接之前的状态,所以在创建新的边的时候,它们都是 null
。很多人在创建新边后获取 sourceCell
时误用了 previousCell
,正确的使用方式是:
graph.on('edge:connected', ({ isNew, edge }) => {
if (isNew) {
const source = edge.getSourceCell()
}
})
节点/边
添加/删除/修改
当节点/边被添加到画布时,触发以下事件:
added
cell:added
node:added
(仅当 cell 是节点时才触发)edge:added
(仅当 cell 是边时才触发)
当节点/边被移除时,触发以下事件:
removed
cell:removed
node:removed
(仅当 cell 是节点时才触发)edge:removed
(仅当 cell 是边时才触发)
当节点/边发生任何改变时,触发以下事件:
changed
cell:changed
node:changed
(仅当 cell 是节点时才触发)edge:changed
(仅当 cell 是边时才触发)
可以在节点/边上监听:
cell.on('added', ({ cell, index, options }) => { })
cell.on('removed', ({ cell, index, options }) => { })
cell.on('changed', ({ cell, options }) => { })
或者在 Graph 上监听:
graph.on('cell:added', ({ cell, index, options }) => { })
graph.on('cell:removed', ({ cell, index, options }) => { })
graph.on('cell:changed', ({ cell, options }) => { })
graph.on('node:added', ({ node, index, options }) => { })
graph.on('node:removed', ({ node, index, options }) => { })
graph.on('node:changed', ({ node, options }) => { })
graph.on('edge:added', ({ edge, index, options }) => { })
graph.on('edge:removed', ({ edge, index, options }) => { })
graph.on('edge:changed', ({ edge, options }) => { })
change:xxx
当调用 setXxx(val, options)
和 removeXxx(options)
方法去改变节点/边的数据时,并且 options.silent
不为 true
时,都将触发对应的 chang
事件,并触发节点/边重绘。例如:
cell.setZIndex(2)
cell.setZIndex(2, { silent: false })
cell.setZIndex(2, { anyKey: 'anyValue' })
将触发 Cell 上的以下事件:
change:*
change:zIndex
和 Graph 上的以下事件:
cell:change:*
node:change:*
(仅当 cell 是节点时才触发)edge:change:*
(仅当 cell 是边时才触发)cell:change:zIndex
node:change:zIndex
(仅当 cell 是节点时才触发)edge:change:zIndex
(仅当 cell 是边时才触发)
可以在节点/边上监听:
// 当 cell 发生任何改变时都将被触发,可以通过 key 来确定改变项
cell.on('change:*', (args: {
cell: Cell
key: string // 通过 key 来确定改变项
current: any // 当前值
previous: any // 改变之前的值
options: any // 透传的 options
}) => {
if (key === 'zIndex') {
//
}
})
cell.on('change:zIndex', (args: {
cell: Cell
current?: number // 当前值
previous?: number // 改变之前的值
options: any // 透传的 options
}) => { })
或者在 Graph 上监听:
graph.on('cell:change:*', (args: {
cell: Cell
key: string // 通过 key 来确定改变项
current: any // 当前值,类型根据 key 指代的类型确定
previous: any // 改变之前的值,类型根据 key 指代的类型确定
options: any // 透传的 options
}) => { })
// 当 cell 为节点时触发
graph.on('node:change:*', (args: {
cell: Cell
node: Node
key: string // 通过 key 来确定改变项
current: any // 当前值,类型根据 key 指代的类型确定
previous: any // 改变之前的值,类型根据 key 指代的类型确定
options: any // 透传的 options
}) => { })
// 当 cell 为边时触发
graph.on('edge:change:*', (args: {
cell: Cell
edge: Edge
key: string // 通过 key 来确定改变项
current: any // 当前值,类型根据 key 指代的类型确定
previous: any // 改变之前的值,类型根据 key 指代的类型确定
options: any // 透传的 options
}) => { })
graph.on('cell:change:zIndex', (args: {
cell: Cell
current?: number // 当前值
previous?: number // 改变之前的值
options: any // 透传的 options
}) => { })
// 当 cell 为节点时触发
graph.on('node:change:zIndex', (args: {
cell: Cell
node: Node
current?: number // 当前值
previous?: number // 改变之前的值
options: any // 透传的 options
}) => { })
// 当 cell 为边时触发
graph.on('edge:change:zIndex', (args: {
cell: Cell
edge: Edge
current?: number // 当前值
previous?: number // 改变之前的值
options: any // 透传的 options
}) => { })
其他 change
事件如下列表,回调函数的参数与上面提到的 change:zIndex
的参数结构一致。
-
Cell
change:*
change:attrs
change:zIndex
change:markup
change:visible
change:parent
change:children
change:view
change:data
-
Node
change:size
change:angle
change:position
change:ports
change:portMarkup
change:portLabelMarkup
change:portContainerMarkup
ports:added
ports:removed
-
Edge
change:source
change:target
change:terminal
change:router
change:connector
change:vertices
change:labels
change:defaultLabel
change:toolMarkup
change:doubleToolMarkup
change:vertexMarkup
change:arrowheadMarkup
vertexs:added
vertexs:removed
labels:added
labels:removed
除了上述这些内置的 Key,我们也支持监听自定义的 Key,例如
cell.on('change:custom', ({ cell, current, previous, options}) => {
console.log(current)
})
当通过 cell.prop('custom', 'any data')
方法修改 custom
属性的值时将触发 change:custom
事件。
动画
'transition:start'
动画开始时触发'transition:progress'
动画过程中触发'transition:complete'
动画完成时触发'transition:stop'
动画被停止时触发'transition:finish'
动画完成或被停止时触发
cell.on('transition:start', (args: Animation.CallbackArgs) => {})
cell.on('transition:progress', (args: Animation.ProgressArgs) => {})
cell.on('transition:complete', (args: Animation.CallbackArgs) => {})
cell.on('transition:stop', (args: Animation.StopArgs) => {})
cell.on('transition:finish', (args: Animation.CallbackArgs) => {})
graph.on('cell:transition:start', (args: Animation.CallbackArgs) => {})
graph.on('cell:transition:progress', (args: Animation.ProgressArgs) => {})
graph.on('cell:transition:complete', (args: Animation.CallbackArgs) => {})
graph.on('cell:transition:stop', (args: Animation.StopArgs) => {})
graph.on('cell:transition:finish', (args: Animation.CallbackArgs) => {})
graph.on('node:transition:start', (args: Animation.CallbackArgs) => {})
graph.on('node:transition:progress', (args: Animation.ProgressArgs) => {})
graph.on('node:transition:complete', (args: Animation.CallbackArgs) => {})
graph.on('node:transition:stop', (args: Animation.StopArgs) => {})
graph.on('node:transition:finish', (args: Animation.CallbackArgs) => {})
graph.on('edge:transition:start', (args: Animation.CallbackArgs) => {})
graph.on('edge:transition:progress', (args: Animation.ProgressArgs) => {})
graph.on('edge:transition:complete', (args: Animation.CallbackArgs) => {})
graph.on('edge:transition:stop', (args: Animation.StopArgs) => {})
graph.on('edge:transition:finish', (args: Animation.CallbackArgs) => {})