我正在使用d3.js创建大量的svg:ellipse元素(~5000)。在初始渲染之后,可以通过后端更新一些数据项(我将知道哪些数据项),并且我想更改这些省略号的颜色(例如)。
是否有快速方法来恢复与数据项或项相关联的DOM元素?除了明显的技术,如果使用数据子集重新计算整个DOM元素集合的连接?
var myData = [{ id: 'item1'}, { id: 'item2' }, ... { id: 'item5000' }];
var create = d3.selectAll('ellipse).data(myData, function(d) { return d.id; });
create.enter().append('ellipse').each(function(d) {
// initialize ellipse
});
// later on
// this works, but it seems like it would have to iterate over all 5000 elements
var subset = myData.slice(1200, 1210); // just an example
var updateElements = d3.selectAll('ellipse').data(subset, function(d) { return d.id; });
updateElements.each(function(d) {
// this was O(5000) to do the join, I _think_
// change color or otherwise update
});
我每秒都会多次渲染更新(尽可能快,真的如此),似乎O(5000)更新了一些元素很多。
我在考虑这样的事情:
create.enter().append('ellipse').each(function(d) {
d.__dom = this;
// continue with initialization
});
// later on
// pull the dom nodes back out
var subset = myData.slice(1200, 1210).map(function(d) { return d.__dom; });
d3.selectAll(subset).each(function(d) {
// now it should be O(subset.length)
});
这很有效。但似乎这是一个常见的模式,所以我想知道是否有一种标准方法来解决这个问题?我实际上想在多个渲染中使用我的数据,所以我需要更加聪明,这样他们就不会相互绊倒。
基本上,我知道d3提供了来自DOM的地图 - >数据来自domElement。__data__
。有没有一种快速简便的方法来计算反向映射,除了手动缓存值?
我需要从数据中获取 - > DOM。
答案 0 :(得分:3)
只要您保持d3选择引用处于活动状态(示例中为create
),D3就会使用映射将数据键映射到更新中的DOM节点,因此它实际上是O(log n)。
我们可以使用D3更新/数据运算符方法对子集上的循环方法进行一些测试:
var d3UpdateMethod = function() {
svg.selectAll("ellipse").data(subset, keyFunc)
.attr("style", "fill:green");
}
var loopMethod = function() {
for (var i=0; i < subset.length; i++) {
svg.selectAll(".di" + i)
.attr("style", "fill:green");
}
}
var timedTest = function(f) {
var sumTime=0;
for (var i=0; i < 10; i++) {
var startTime = Date.now();
f();
sumTime += (Date.now() - startTime);
}
return sumTime / 10;
};
var nextY = 100;
var log = function(text) {
svg.append("text")
.attr("x", width/2)
.attr("y", nextY+=100)
.attr("text-anchor", "middle")
.attr("style", "fill:red")
.text(text);
};
log("d3UpdateMethod time:" + timedTest(d3UpdateMethod));
log("loopMethod time:" + timedTest(loopMethod));
我还创造了一个小提琴来展示我理解你正在尝试做的事情here。
使跟踪子集中的节点变得容易的另一种方法是向子集添加CSS类。例如:
var ellipse = svg.selectAll("ellipse").data(data, keyFunc).enter()
.append("ellipse")
.attr("class", function (d) {
var cl = "di" + d.i;
if (d.i % 10 == 0)
cl+= " subset"; //<< add css class for those nodes to be updated later
return cl;
})
...
请注意,“子集”类如何仅添加到您知道的子集中的节点,以便稍后更新。然后,您可以稍后选择它们以进行更新,其中包含以下内容:
svg.selectAll("ellipse.subset").attr("style", "fill:yellow");
我更新了fiddle以包含此测试,它几乎与directMethod一样快。