更改在 d3 中绘制数据的顺序
Change order in which data is drawn in d3
我有一些数据 (Node
s) 需要绘制。这些节点可以重叠,因此绘制它们的顺序很重要(应该显示在最上面的需要最后绘制)。
这些节点的位置和相应的 z 轴可能会发生变化,这就是为什么我试图通过使用 key 来模拟这种行为,它结合了当前 index 存储节点的列表。
case class Node(id: Int)
def addNodesToSVG = {
val sortedData: List[Node] = ???
val nodesSelection = d3.select("#nodes").selectAll(".node")
.data(sortedData.toJSArray, (n: Node) => {
n.id.toString +
// the position of those nodes may change over time
// that's why we need to include the position in the identifier
sortedData.indexOf(n)
}
nodesSelection.enter().append("g").attr("class", "node") // ...
nodesSelection
.attr("transform", transform) // ...
nodesSelection.exit().remove()
}
不幸的是,这似乎没有按预期工作。
理论上,如果我只有两个节点(n1
和 n2
),这就是我认为这将起作用的方式,它们保存在 List(n1, n2)
node key
----- ---
n1 10 // node 1 at position 0
n2 21 // node 2 at position 1
现在,如果我将列表更改为 List(n2, n1)
并再次调用 addNodesToSVG
,这就是我认为会发生的情况:
node key
----- ---
n2 20 // node 1 at position 0
n1 12 // node 2 at position 1
由于这些未知,我认为它会删除 (nodesSelection.exit().remove()
) 旧节点并以正确的顺序绘制 'new' 节点。然而,这并没有发生。为什么?
编辑 经过更多调试后,我发现我的 exit
选择总是大小为 0。
我认为应该以一致的方式使用 id 函数——仅仅因为一个对象改变了它的位置,id 函数在它上面的结果不应该改变(我认为这是使用的全部意义它首先)。我采用的方法是使 id 函数完全依赖于节点的 id;向指定渲染顺序的数据对象添加一个字段;合并后根据新字段对选择进行排序。
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<button onclick="sw()">Switch</button>
<script>
var d1 = [{
id: 'a',
z: 1,
fill: 'red',
y: 0
}, {
id: 'b',
z: 2,
fill: 'green',
y: 5
}];
var d2 = [{
id: 'a',
z: 2,
fill: 'red',
y: 5
}, {
id: 'b',
z: 1,
fill: 'green',
y: 0
}]
var current = 0;
var svg = d3.select("body").append("svg")
.attr("width", 100)
.attr("height", 100)
.attr('viewBox', '0 0 10 20');
function render(d) {
var r = svg.selectAll('rect')
.data(d, function(d) { return d.id; });
r.enter()
.append('rect')
.attr('width', 10)
.attr('height', 10)
.attr('fill', function(d) { return d.fill; })
.merge(r)
.sort(function(r1, r2) {
if (r1.z > r2.z) return 1;
if (r1.z < r2.z) return -1;
return 0;
})
.transition()
.attr('y', function(d) { return d.y; });
r.exit().remove();
};
function sw() {
if (current == 0) {
current = 1;
render(d2);
} else {
current = 0;
render(d1);
}
}
render(d1);
</script>
</body>
我有一些数据 (Node
s) 需要绘制。这些节点可以重叠,因此绘制它们的顺序很重要(应该显示在最上面的需要最后绘制)。
这些节点的位置和相应的 z 轴可能会发生变化,这就是为什么我试图通过使用 key 来模拟这种行为,它结合了当前 index 存储节点的列表。
case class Node(id: Int)
def addNodesToSVG = {
val sortedData: List[Node] = ???
val nodesSelection = d3.select("#nodes").selectAll(".node")
.data(sortedData.toJSArray, (n: Node) => {
n.id.toString +
// the position of those nodes may change over time
// that's why we need to include the position in the identifier
sortedData.indexOf(n)
}
nodesSelection.enter().append("g").attr("class", "node") // ...
nodesSelection
.attr("transform", transform) // ...
nodesSelection.exit().remove()
}
不幸的是,这似乎没有按预期工作。
理论上,如果我只有两个节点(n1
和 n2
),这就是我认为这将起作用的方式,它们保存在 List(n1, n2)
node key
----- ---
n1 10 // node 1 at position 0
n2 21 // node 2 at position 1
现在,如果我将列表更改为 List(n2, n1)
并再次调用 addNodesToSVG
,这就是我认为会发生的情况:
node key
----- ---
n2 20 // node 1 at position 0
n1 12 // node 2 at position 1
由于这些未知,我认为它会删除 (nodesSelection.exit().remove()
) 旧节点并以正确的顺序绘制 'new' 节点。然而,这并没有发生。为什么?
编辑 经过更多调试后,我发现我的 exit
选择总是大小为 0。
我认为应该以一致的方式使用 id 函数——仅仅因为一个对象改变了它的位置,id 函数在它上面的结果不应该改变(我认为这是使用的全部意义它首先)。我采用的方法是使 id 函数完全依赖于节点的 id;向指定渲染顺序的数据对象添加一个字段;合并后根据新字段对选择进行排序。
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<button onclick="sw()">Switch</button>
<script>
var d1 = [{
id: 'a',
z: 1,
fill: 'red',
y: 0
}, {
id: 'b',
z: 2,
fill: 'green',
y: 5
}];
var d2 = [{
id: 'a',
z: 2,
fill: 'red',
y: 5
}, {
id: 'b',
z: 1,
fill: 'green',
y: 0
}]
var current = 0;
var svg = d3.select("body").append("svg")
.attr("width", 100)
.attr("height", 100)
.attr('viewBox', '0 0 10 20');
function render(d) {
var r = svg.selectAll('rect')
.data(d, function(d) { return d.id; });
r.enter()
.append('rect')
.attr('width', 10)
.attr('height', 10)
.attr('fill', function(d) { return d.fill; })
.merge(r)
.sort(function(r1, r2) {
if (r1.z > r2.z) return 1;
if (r1.z < r2.z) return -1;
return 0;
})
.transition()
.attr('y', function(d) { return d.y; });
r.exit().remove();
};
function sw() {
if (current == 0) {
current = 1;
render(d2);
} else {
current = 0;
render(d1);
}
}
render(d1);
</script>
</body>