mxGraph显示隐藏sizer(拖拽缩放点)

这篇文章的主题是如何自定义 mxGraph 图元的 sizer 的显示与隐藏。

显示与隐藏分两种情况,一种是在首次渲染时,就按照规则显示/隐藏 sizer,一种是更新时,让 sizer 的显示/隐藏状态发生变化。下面依次分析。

显示/隐藏 sizer 首先要搞明白 sizer 是什么。sizer 本质上还是 shape,继承自 mxShape,所以按照 shape 的显示与隐藏就可以完成 sizer 的显示与隐藏。

那接下来我们看看 mxGraph 自己是如何完成这个功能的。

代码分析

sizer 用来控制 shape 缩放,这里以块图元为例。

我们要知道这些东西(图元的缩放)都存在于 vertexHandler 中。所以我们从 vertexHanlder 的方法中,可以看到进行了初始化,调用了 init 方法。

mxVertexHandler.js
Copy

_8
function mxVertexHandler(state) {
_8
if (state != null) {
_8
this.state = state;
_8
this.init();
_8
_8
// ......
_8
}
_8
};

每一个图元,都对应一个 vertexHandler。每次 new vertexHanlder 时,都会执行 init 方法,此时 sizer 就已经被创建出来了。

在 init 方法中,有这么一段代码是用来创建 sizer 的


_15
var i = 0;
_15
_15
if (resizable) {
_15
if (!this.singleSizer) {
_15
this.sizers.push(this.createSizer('nw-resize', i++));
_15
this.sizers.push(this.createSizer('n-resize', i++));
_15
this.sizers.push(this.createSizer('ne-resize', i++));
_15
this.sizers.push(this.createSizer('w-resize', i++));
_15
this.sizers.push(this.createSizer('e-resize', i++));
_15
this.sizers.push(this.createSizer('sw-resize', i++));
_15
this.sizers.push(this.createSizer('s-resize', i++));
_15
}
_15
_15
this.sizers.push(this.createSizer('se-resize', i++));
_15
}

这里创建了 8 个 sizer,分别是图元的左上,上,右上,左,右,左下 ,下,右下(如下图所示)

它们之间的顺序是从左到右,从上到下,index 从 0-7。

来看看 createSizer 的实现是什么样的,屏蔽掉一些这里不需要理解的代码。只看对我们有用的信息


_14
mxVertexHandler.prototype.createSizer = function (cursor, index, size, fillColor) {
_14
size = size || mxConstants.HANDLE_SIZE;
_14
_14
var bounds = new mxRectangle(0, 0, size, size);
_14
var sizer = this.createSizerShape(bounds, index, fillColor);
_14
_14
// ......
_14
_14
if (!this.isSizerVisible(index)) {
_14
sizer.visible = false;
_14
}
_14
_14
return sizer;
_14
};

看这里,sizer 实际上是一个 shape,被创建出来后,当达成某个条件后,sizer 的 visible 会被设置为 false。一旦 shape 的 visible 被设置为 false 以后,这个 shape 就不会再被渲染了

visible 控制 shape 渲染的逻辑可以参见 mxShape.redraw 方法

进入 isSizerVisible 方法可以看到 mxGraph 默认只返回了 true,入参为 index。


_3
mxVertexHandler.prototype.isSizerVisible = function (index) {
_3
return true;
_3
};

实现功能

首次渲染

我们可以按照 index 返回 false 或者 true,以达成自定义渲染 sizer 的目的。如果还需要根据图元本身做判断,那么 this 上挂载了 state,可以拿到 state 后,完成自定义渲染 sizer 的目的。

比如我们将上下左右的四个 sizer 移除,可以重写 isSizerVisible 方法。如下:


_5
mxVertexHandler.prototype.isSizerVisible = function(index)
_5
{
_5
if ([1, 3, 4, 6].includes(index)) return false;
_5
return true;
_5
};

指定sizer进行显示/隐藏

mxGraph本身只提供了一个方法setHandlesVisible来显示和隐藏所有的handle,这里的handle是sizer的父级,sizer本身也是handle的一种,只不过是拿来控制图元大小的一种handle,名字叫做sizer。

我们看一下setHandlesVisible方法是如何实现的吧


_14
mxVertexHandler.prototype.setHandlesVisible = function(visible)
_14
{
_14
this.handlesVisible = visible;
_14
_14
if (this.sizers != null)
_14
{
_14
for (var i = 0; i < this.sizers.length; i++)
_14
{
_14
this.sizers[i].node.style.display = (visible) ? '' : 'none';
_14
}
_14
}
_14
_14
// ......
_14
};

依旧是省略掉一部分代码。这里直接控制了dom上的样式,对sizer对应的dom元素的display样式进行了控制,所以如果想要实现只显示一部分的代码,可以自己写一个方法。但在此之前我们要搞明白handler的调用路径。

当我们点击一个图元时,selectionModel会发生变化,继而引起selectionCellsHandler中刷新handlers,所以在selectionCellsHandler中存储了所有已被创建的handler。在这里我们可以拿到,我们想操作的cell对应的handler。继而就可以完成对指定sizer(handle)的显示隐藏。

获取handler的方法我们可以在selectionCellsHandler中找到,即getHandler

理论可行,开始写代码


_6
const hideSizerByIndex = (graph, cell, visible, index) => {
_6
if (index !== null) {
_6
const handler = graph.selectionCellsHandler.getHandler(cell);
_6
handler.sizers[i].node.style.display = (visible) ? '' : 'none';
_6
}
_6
}

这样就完成了指定sizer进行显示/隐藏

至此,就可以自定义 sizer 的显示与隐藏了

后续将会写更多与mxGraph相关的文章,敬请期待。