在《mxgraph 系列【3】: 底层状态树 mxCell》,有同学提到一个疑问:
大致上,是需要修改拖拽功能,从下图左边样式更改为右边样式:
简单翻了一下代码,找到解决方案:
1. 拖拽的实现
首先,需要简单了解一下mxGraph是如何实现拖拽的。在 mxGraph 内部有一个 mxGraphHandler
类专门用于监听鼠标 keydown、mousemove、keyup 等事件,当用户选中某个图形并执行鼠标拖拽后,会触发 mxGraphHandler.prototype.mouseMove
回调,函数内部执行一堆判断逻辑,包括:
- 是否在复制元素
- 是否在将元素drop到其他容器上
- 是否实时预览
- ...
逻辑太复杂了,总结下来就是,拖拽预览功能有两种模式,一是创建一个与拖拽原始宽高位置相同的矩形,在该矩形上施加位置变化,姑且称为“矩形预览模式”;二是直接在原始元素上施加位置变化,姑且称为“实时预览”。两者主要通过 mxGraphHandler
实例的 livePreviewUsed
属性进行判断。
判断的位置,一是在 mouseMove 回调中:
mxGraphHandler.prototype.mouseMove = function(sender, me){
...
// https://github.com/jgraph/mxgraph/blob/master/javascript/src/js/handler/mxGraphHandler.js#L901
if (!this.livePreviewUsed && this.shape == null) {
this.shape = this.createPreviewShape(this.bounds);
}
...
}
二是在更新预览图形的处理函数中:
// https://github.com/jgraph/mxgraph/blob/master/javascript/src/js/handler/mxGraphHandler.js#L1036
mxGraphHandler.prototype.updatePreview = function(remote) {
if (this.livePreviewUsed && !remote) {
if (this.cells != null)
{
this.setHandlesVisibleForCells(this.graph.getSelectionCells(), false);
this.updateLivePreview(this.currentDx, this.currentDy);
}
}
else
{
this.updatePreviewShape();
}
};
那么,理论上在mouseMove回调之前设置 this.livePreviewUsed = true
就能强制切换为实时预览效果。
2. 修改代码
在上面理论基础上,我们只需要扩展原有的 mxGraphHandler.prototype.mouseMove
函数就可以达到目标:
const oldFunc = mxGraphHandler.prototype.mouseMove;
mxGraphHandler.prototype.mouseMove = function (sender, me) {
var graph = this.graph;
if (
!me.isConsumed() &&
graph.isMouseDown &&
this.cell != null &&
this.first != null &&
this.bounds != null &&
!this.suspended
) {
this.livePreviewUsed = true;
}
oldFunc.call(this, sender, me);
};
注意
这段代码需要放在创建mxGraph实例之前执行才能生效。
修改后,运行效果:
emmm,效果不太理想,因为拖拽过程中,原型的操作点(绿色的点)没有随鼠标移动,继续翻代码,发现上面说的 updatePreview
函数只是修改了model状态,还没有推送到页面渲染,实际执行渲染的位置是 mxGraphHandler
文件的第 1214 行:
mxGraphHandler.prototype.mouseMove = function (sender, me) {
...
// https://github.com/jgraph/mxgraph/blob/master/javascript/src/js/handler/mxGraphHandler.js#L1214
this.graph.view.validate();
this.redrawHandles(states);
this.resetPreviewStates(states);
...
}
那么,只在渲染之前执行 this.setHandlesVisibleForCells(this.graph.getSelectionCells(), false);
调用,更新 handlers
数组的位置就可以实现操作点随拖拽移动的效果:
嗯,效果不错。但是这里只能更改 javascript/src/js/handler/mxGraphHandler.js
源码文件才能实现,请根据项目情况决定是否执行此变更吧。修改后代码应该入:
mxGraphHandler.prototype.mouseMove = function (sender, me) {
...
// https://github.com/jgraph/mxgraph/blob/master/javascript/src/js/handler/mxGraphHandler.js#L1214
this.setHandlesVisibleForCells(this.graph.getSelectionCells(), false);
this.graph.view.validate();
this.redrawHandles(states);
this.resetPreviewStates(states);
...
}