【发布时间】:2021-07-19 18:45:03
【问题描述】:
在我的d3 图表中,我有一个要渲染的顶点列表以及附加的力模拟,以及顶点的拖动行为。这工作正常。但是,由于顶点可以重叠,我想更改拖动机制,使拖动的顶点在拖动操作期间成为最顶部的 SVG 元素。我通过像这样更新拖动开始回调并将此行为分配给创建的每个顶点来完成此操作:
const fnDrag = drag()
.on('start', function(d: Data) {
simulation.alphaTarget(0.3).restart();
select(select(this).node().parentNode).raise();
d.fx = d.x;
d.fy = d.y;
...
})
.on('drag', ...)
.on('end', ...);
问题是模拟的tick回调是这样定义的:
const simulation = forceSimulation(data)
.force('...', ...)
.force('...', ...)
.on('tick', () => {
vertexElements.selectAll('g')
.data(data, d => d.key)
.join(
enter => {
return enter.append('g');
},
update => {
return update.attr('translate', d => `translate(${d.x}, ${d.y})`);
},
exit => {
return exit.remove();
}
);
});
这会导致dragStarted 回调和onTick 回调之间出现某种竞争条件。 dragStarted 回调将拖动的 SVG 元素提升为父元素的最后一个元素,而 onTick 回调呈现 data 数组,以便 SVG 元素按照输入数组中给定的顺序呈现(即不提升)。
我通过检查 Chrome 中的元素层次结构验证了这一点,我发现在拖动过程中,被拖动的元素会迅速改变位置,其顺序由输入数组和最后一个子元素决定。
如何正确实现 raise 功能?我是否必须求助于手动修改 dragStarted 处理程序中的输入数组,以便拖动的元素数据是列表中的最后一项?这似乎是个坏主意。另外,我希望 Z 顺序在停止拖动后返回到其初始状态。
我也尝试在顶点数据结构中保持拖动状态,在 dragStart 和 dragEnd 回调期间设置和取消设置标志,并在 onTick 处理程序中使用selection.sort((a, b) => a.dragging ? +1 : -1) 在绘图循环结束时强制拖动顶点,但是这根本不会渲染任何东西。选择显然变为空。
【问题讨论】:
标签: d3.js