使用 d3.join 时是否可以不对元素重新排序?
Is it possible to not reorder elements when using d3.join?
在 d3
中,我们可以更改选择中元素的顺序,例如使用 raise
.
然而,当我们重新绑定数据并使用join
时,这个顺序被丢弃了。
当我们使用“旧方法”绑定数据时,不会发生这种情况,使用 enter
和 merge
。
请参阅下面的 fiddle,您可以在其中单击圆圈(例如蓝色圆圈)将其置于最前面。当您单击“重绘”时,使用 join
时圆圈会恢复到原来的 z 顺序,但使用 enter
和 merge
时则不会.
我能否实现圆圈保持其 z 顺序并仍然使用 join
?
const data = [{
id: 1,
v: 10,
c: 'red'
}, {
id: 2,
v: 30,
c: 'blue'
}, {
id: 3,
v: 60,
c: 'green'
}]
let nDrawCall = 0
function redraw() {
nDrawCall++
//svg1 with old enter-merge pattern that works
const circles = d3.select('#svg1')
.selectAll('circle')
.data(data, d => d.id)
circles
.enter()
.append('circle')
.on('click', function() {
d3.select(this).raise()
})
.merge(circles)
.attr('cx', d => d.v * nDrawCall)
.attr('cy', d => d.v)
.attr('r', d => d.v)
.attr('fill', d => d.c)
//svg2 with new join pattern that sadly reorders
d3.select('#svg2')
.selectAll('circle')
.data(data, d => d.id)
.join(enter => enter
.append('circle')
.on('click', function() {
d3.select(this).raise()
})
)
.attr('cx', d => d.v * nDrawCall)
.attr('cy', d => d.v)
.attr('r', d => d.v)
.attr('fill', d => d.c)
}
function reset() {
nDrawCall = 0
redraw()
}
redraw()
/*
while (true) {
iter++
console.log(iter)
sleepFor(500)
}
*/
svg {
height: 100px;
width: 100%;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
</head>
<body>
<button onclick="redraw()">
Redraw
</button>
<button onclick="reset()">
Reset
</button>
<div>
<svg id="svg1" />
<svg id="svg2" />
</div>
</body>
</html>
join
在合并 enter- 和 update-selection 后执行隐式 order
,请参阅 https://github.com/d3/d3-selection/blob/91245ee124ec4dd491e498ecbdc9679d75332b49/src/selection/join.js#L14。
你的例子中数据绑定后的选择顺序,即使文档顺序改变了,依然是红蓝绿。因此,使用 join
.
将圆圈重新排序为原始顺序
您可以通过更改反映文档顺序更改的数据绑定来解决这个问题。我在这里通过将单击圆的数据移动到数据数组的末尾来做到这一点。
let data = [{
id: 1,
v: 10,
c: 'red'
}, {
id: 2,
v: 30,
c: 'blue'
}, {
id: 3,
v: 60,
c: 'green'
}]
let nDrawCall = 0
function redraw() {
nDrawCall++
d3.select('#svg2')
.selectAll('circle')
.data(data, d => d.id)
.join(enter => enter
.append('circle')
.on('click', function() {
const circle = d3.select(this).raise();
data.push(data.splice(data.indexOf(circle.datum()), 1)[0]);
})
)
.attr('cx', d => d.v * nDrawCall)
.attr('cy', d => d.v)
.attr('r', d => d.v)
.attr('fill', d => d.c)
}
function reset() {
nDrawCall = 0
redraw()
}
redraw()
svg {
height: 100px;
width: 100%;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
</head>
<body>
<button onclick="redraw()">
Redraw
</button>
<button onclick="reset()">
Reset
</button>
<div>
<svg id="svg2" />
</div>
</body>
</html>
在 d3
中,我们可以更改选择中元素的顺序,例如使用 raise
.
然而,当我们重新绑定数据并使用join
时,这个顺序被丢弃了。
当我们使用“旧方法”绑定数据时,不会发生这种情况,使用 enter
和 merge
。
请参阅下面的 fiddle,您可以在其中单击圆圈(例如蓝色圆圈)将其置于最前面。当您单击“重绘”时,使用 join
时圆圈会恢复到原来的 z 顺序,但使用 enter
和 merge
时则不会.
我能否实现圆圈保持其 z 顺序并仍然使用 join
?
const data = [{
id: 1,
v: 10,
c: 'red'
}, {
id: 2,
v: 30,
c: 'blue'
}, {
id: 3,
v: 60,
c: 'green'
}]
let nDrawCall = 0
function redraw() {
nDrawCall++
//svg1 with old enter-merge pattern that works
const circles = d3.select('#svg1')
.selectAll('circle')
.data(data, d => d.id)
circles
.enter()
.append('circle')
.on('click', function() {
d3.select(this).raise()
})
.merge(circles)
.attr('cx', d => d.v * nDrawCall)
.attr('cy', d => d.v)
.attr('r', d => d.v)
.attr('fill', d => d.c)
//svg2 with new join pattern that sadly reorders
d3.select('#svg2')
.selectAll('circle')
.data(data, d => d.id)
.join(enter => enter
.append('circle')
.on('click', function() {
d3.select(this).raise()
})
)
.attr('cx', d => d.v * nDrawCall)
.attr('cy', d => d.v)
.attr('r', d => d.v)
.attr('fill', d => d.c)
}
function reset() {
nDrawCall = 0
redraw()
}
redraw()
/*
while (true) {
iter++
console.log(iter)
sleepFor(500)
}
*/
svg {
height: 100px;
width: 100%;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
</head>
<body>
<button onclick="redraw()">
Redraw
</button>
<button onclick="reset()">
Reset
</button>
<div>
<svg id="svg1" />
<svg id="svg2" />
</div>
</body>
</html>
join
在合并 enter- 和 update-selection 后执行隐式 order
,请参阅 https://github.com/d3/d3-selection/blob/91245ee124ec4dd491e498ecbdc9679d75332b49/src/selection/join.js#L14。
你的例子中数据绑定后的选择顺序,即使文档顺序改变了,依然是红蓝绿。因此,使用 join
.
您可以通过更改反映文档顺序更改的数据绑定来解决这个问题。我在这里通过将单击圆的数据移动到数据数组的末尾来做到这一点。
let data = [{
id: 1,
v: 10,
c: 'red'
}, {
id: 2,
v: 30,
c: 'blue'
}, {
id: 3,
v: 60,
c: 'green'
}]
let nDrawCall = 0
function redraw() {
nDrawCall++
d3.select('#svg2')
.selectAll('circle')
.data(data, d => d.id)
.join(enter => enter
.append('circle')
.on('click', function() {
const circle = d3.select(this).raise();
data.push(data.splice(data.indexOf(circle.datum()), 1)[0]);
})
)
.attr('cx', d => d.v * nDrawCall)
.attr('cy', d => d.v)
.attr('r', d => d.v)
.attr('fill', d => d.c)
}
function reset() {
nDrawCall = 0
redraw()
}
redraw()
svg {
height: 100px;
width: 100%;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
</head>
<body>
<button onclick="redraw()">
Redraw
</button>
<button onclick="reset()">
Reset
</button>
<div>
<svg id="svg2" />
</div>
</body>
</html>