2

我正在使用 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 通过 domElement 提供来自 DOM -> 数据的映射。__data__. 除了手动缓存值之外,是否有一种快速简便的方法来计算反向映射?

我需要从数据-> DOM 中获取。

4

1 回答 1

4

只要您保持 d3 选择引用处于活动状态(create在您的示例中),D3 就会使用映射将数据键映射到更新中的 DOM 节点,因此它实际上是 O(log n)。

我们可以使用 D3 update /data 运算符方法与子集上的循环方法进行一些测试:

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));

我还创建了一个小提琴来展示我理解你在这里尝试做的事情。


另一种便于跟踪子集中节点的方法是向子集中添加 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 一样快。

于 2013-02-16T07:30:40.850 回答