解决antv x6 多选后卡顿问题
最近在使用x6开发组态编辑器,但是发现在多选时拖拽会非常卡顿,甚至于在选择20多个图元的情况下几乎延迟时间将近1秒,虽然很多人都跟官方提交了多选性能问题,但是到目前为止仍然没有得到优化,所以迫不得已只能自己扒源码寻找解决办法,终于找到了提升性能的解决方案。
- 卡顿问题原因
通过测试发现在Selection插件的showNodeSelectionBox和showEdgeSelectionBox属性设置为true时,拖拽就会非常卡顿,当设置为false时同时拖拽400多节点也几乎没有卡顿问题,判断卡顿的问题就出在这个属性配置上
下方是一百个节点在启用设置与禁用设置的对比效果:
启用属性时:
禁用属性时:
禁用属性时400个节点效果:
可以看到流畅度几乎是十几倍的提升,问题的原因就在于当启用此属性时不论选中多少节点都会生成响应数量的选择框,如果选中400个节点则会有400个class为x6-widget-selection-box-node的div
可以看到上面图中搜索到404个class为x6-widget-selection-box-node的div,而且在每次移动时都会将这些div移除并根据最新移动位置重新插入
这就是在选中多个节点时卡顿的主要原因,因为大量的dom操作导致浏览器线程被锁死,直到全部dom更新完毕,而如果不启用showNodeSelectionBox属性,虽然选中依然可以拖拽,但却连最外层大的选择框都不显示,选中未选中都不得而知,体验实在太差,这里实在不知道官方的操作,要关都关要开都开,无法单独配置。
- 解决办法
要解决这个问题只能通过重写覆盖官方方法,使框架调用重绘节点时调用我们的重写方法进行绘制
1、首先禁用掉官方的showNodeSelectionBox属性,使框选时不再绘制大量单节点选择框,只绘制大选择框
js
graph.use(
new Selection({
rubberband: true,
strict: true,
showNodeSelectionBox: false, //是否显示选中边框,此属性会降低大量图元拖拽时的性能
rubberEdge: true,
following: true, //拖拽时图元是否跟随,设置为false可提升大量图元时的拖拽性能
})
);
2、重写覆盖官方代码,使大框在每次移动时重新绘制位置,并使用防抖提升性能
js
import { debounce } from 'lodash';
//获取官方插件对象
let selection = graph.getPlugin('selection');
//重写每次拖拽的更新方法,方法中去掉了原来判断showNodeSelectionBox是否启用的代码,并调用防抖函数
selection.selectionImpl.confirmUpdate = function () {
this.hide();
debounceChangeBox(this);
return 0;
};
//重写selection插件的大框初始化及位置更新方法
selection.selectionImpl.updateContainer = function () {
const origin = { x: Infinity, y: Infinity };
const corner = { x: 0, y: 0 };
const cells = this.collection.toArray();
cells.forEach((cell) => {
const view = this.graph.renderer.findViewByCell(cell);
if (view) {
const bbox = view.getBBox({
useCellGeometry: true
});
origin.x = Math.min(origin.x, bbox.x);
origin.y = Math.min(origin.y, bbox.y);
corner.x = Math.max(corner.x, bbox.x + bbox.width);
corner.y = Math.max(corner.y, bbox.y + bbox.height);
}
});
Dom.css(this.selectionContainer, {
position: 'absolute',
pointerEvents: 'none',
left: origin.x,
top: origin.y,
width: corner.x - origin.x,
height: corner.y - origin.y
});
Dom.attr(
this.selectionContainer,
'data-selection-length',
this.collection.length
);
if (this.collection.length > 1) {
this.showSelected();
}
const boxContent = this.options.content;
if (boxContent) {
if (typeof boxContent === 'function') {
const content = FunctionExt.call(
boxContent,
this.graph,
this,
this.selectionContent
);
if (content) {
this.selectionContent.innerHTML = content;
}
} else {
this.selectionContent.innerHTML = boxContent;
}
}
if (this.collection.length > 0 && !this.container.parentNode) {
Dom.appendTo(this.container, this.graph.container);
} else if (this.collection.length <= 0 && this.container.parentNode) {
this.container.parentNode.removeChild(this.container);
}
};
//大框位置更新防抖函数
let debounceChangeBox = debounce(
(thiz) => {
thiz.updateContainer();
thiz.showSelected();
},
300,
{
leading: false,
trailing: true
}
);
通过上面的方法重写,改变官方更新逻辑,使每次移动时都更新大框位置,我现在使用的@antv/x6-plugin-selection插件版本是2.1.7,后续官方应该会有优化,大家根据自己具体版本判断卡顿是否跟我是同一个问题,有问题请在留言本留言交流
我目前在做的一个组态编辑器github.com/bighhhh/web...
欢迎大家共同交流进步,可加我QQ:623037142