我有一个使用d3 v4的力模拟图。每个节点都绑定到一些数据,我用它们来确定每个节点的半径。
底层绑定数据会定期更新,对于某些节点,它会更改,而对于其他节点,它会保持不变。
我希望能够仅选择绑定数据发生变化的DOM元素,以便我可以在图表上突出显示这些元素。
例如,假设最初我的数据(绑定到forceSimulation节点)是:
data = [{id: 1, type: 0}, {id: 2, type: 1}]
然后更新为:
data = [{id: 1, type: 1}, {id: 2, type: 1}]
我希望能够只选择与id = 1对应的DOM元素,以便我可以暂时进行颜色更改。
更新选择包含id = 1和id = 2 - 我可以维护先前数据值的内部映射并进行比较,但这似乎效率低下。
谢谢, 亚当
答案 0 :(得分:1)
如果可以检查单个基准属性以查看绑定数据是否已更改,则可以使用selection.property
和自定义属性(如type
)将该属性作为属性进行跟踪。附加数据时,您可以非常轻松地定义属性:
.append("circle")
.property("type",function(d) { return d.type; });
然后,在更新时,您可以根据与属性匹配或不匹配的数据进行过滤:
circles.data(newdata)
.filter(function(d) {
return d.type != d3.select(this).property("type")
})
此过滤器将返回已更改其类型的元素。现在我们可以重新分配属性以反映新类型并转换那些过滤后的元素。
下面的代码段应该证明这一点,数据只是一个或两个(用蓝色和橙色表示),用于设置属性类型。点击svg更新数据,只有那些改变其基准的圆圈会暂时改变它们的半径,同时也会改变它们的颜色以反映它们的新数据。
var svg = d3.select("body")
.append("svg")
.attr("width",400)
.attr("height",400);
var circles = svg.selectAll("circle")
.data(data())
.enter("circle")
.append("circle")
.attr("cy",function(d,i) {
return Math.floor(i/5) * 40 + 20;
})
.attr("cx", function(d,i) {
return i%5 * 40 + 20
})
.attr("r", 8)
.attr("fill",function(d) { return (d) ? "steelblue" : "orange"})
.property("type",function(d) { return d; });
// update on click:
svg.on("click", function() {
circles.data(data())
.filter(function(d) {
return d != d3.select(this).property("type") // filter out unchanged data
})
.property("type",function(d) { return d; }) // update to reflect new data
.transition()
.attr("r", 20)
.attr("fill","crimson")
.duration(500)
.transition()
.attr("fill",function(d) { return (d) ? "steelblue" : "orange" })
.attr("r",8)
.duration(500);
})
function data() {
var output = [];
d3.range(20).map(function(d) {
output.push(Math.round(Math.random()));
})
return output;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>