d3.js:为什么没有数据更新?

时间:2014-05-07 23:55:06

标签: d3.js

我在D3.js中有一个相当简单的可重复使用的图表 - 一些圆圈和一些文字。

我正在努力弄清楚如何使用新数据更新图表,而无需重新绘制整个图表。

使用当前脚本,我可以看到新数据绑定到svg元素,但没有任何数据驱动的文本或属性正在更新。 为什么图表不会更新?

这是一个小提琴:http://jsfiddle.net/rolfsf/em5kL/1/

我这样称呼图表:

d3.select('#clusters')
.datum({
    Name: 'Total Widgets',
    Value: 224,
    Clusters: [
        ['Other', 45],
        ['FooBars', 30],
        ['Foos', 50],
        ['Bars', 124],
        ['BarFoos', 0]
    ]
})
.call( clusterChart() );

点击按钮后,我只是再次调用图表,使用不同的数据

    $("#doSomething").on("click", function(){

    d3.select('#clusters')
        .datum({
            Name: 'Total Widgets',
            Value: 122,
            Clusters: [
                ['Other', 14],
                ['FooBars', 60],
                ['Foos', 22],
                ['Bars', 100],
                ['BarFoos', 5]
            ]
        })
        .call( clusterChart() );

});

图表脚本:

function clusterChart() {
var width = 450,
    margin = 0,
    radiusAll = 72,
    maxRadius = radiusAll - 5,
    r = d3.scale.linear(),
    padding = 1, 
    height = 3 * (radiusAll*2 + padding),
    startAngle = Math.PI / 2,
    onTotalMouseOver = null,
    onTotalClick = null,
    onClusterMouseOver = null,
    onClusterClick = null;
    val = function(d){return d};

function chart(selection) {
    selection.each(function(data) {

        var cx = width / 2,
            cy = height / 2,
            stepAngle = 2 * Math.PI / data.Clusters.length,
            outerRadius = 2*radiusAll + padding;

        r = d3.scale.linear()
                    .domain([0, d3.max(data.Clusters, function(d){return d[1];})])
                    .range([50, maxRadius]);

        var svg = d3.select(this).selectAll("svg")
                    .data([data])
                    .enter().append("svg");

        //enter
        var totalCircle = svg.append("circle")
                    .attr("class", "total-cluster")
                    .attr('cx', cx)
                    .attr('cy', cy)
                    .attr('r', radiusAll)
                    .on('mouseover', onTotalMouseOver)
                    .on('click', onTotalClick);

        var totalName = svg.append("text")
                    .attr("class", "total-name")
                    .attr('x', cx)
                    .attr('y', cy + 16);

        var totalValue = svg.append("text")
                    .attr("class", "total-value")
                    .attr('x', cx)
                    .attr('y', cy + 4);



        var clusters =  svg.selectAll('circle.cluster')
                    .data(data.Clusters)
                    .enter().append('circle')
                    .attr("class", "cluster");

        var clusterValues = svg.selectAll("text.cluster-value")
                    .data(data.Clusters)
                    .enter().append('text')
                    .attr('class', 'cluster-value');

        var clusterNames = svg.selectAll("text.cluster-name")
                    .data(data.Clusters)
                    .enter().append('text')
                    .attr('class', 'cluster-name');


        clusters    .attr('cx', function(d, i) { return cx + Math.cos(startAngle + stepAngle * i) * outerRadius; })
                    .attr('cy', function(d, i) { return cy + Math.sin(startAngle + stepAngle * i) * outerRadius; })
                    .attr("r", "10")
                    .on('mouseover', function(d, i, j) {
                        if (onClusterMouseOver != null) onClusterMouseOver(d, i, j);
                    })
                    .on('mouseout', function() { /*do something*/ })
                    .on('click', function(d, i){ onClusterClick(d); });  

        clusterNames
                    .attr('x', function(d, i) { return cx + Math.cos(startAngle + stepAngle * i) * outerRadius; })
                    .attr('y', function(d, i) { return cy + Math.sin(startAngle + stepAngle * i) * outerRadius + 16; });

        clusterValues  
                    .attr('x', function(d, i) { return cx + Math.cos(startAngle + stepAngle * i) * outerRadius; })
                    .attr('y', function(d, i) { return cy + Math.sin(startAngle + stepAngle * i) * outerRadius - 4; });


        //update with data
        svg         .selectAll('text.total-value')
                    .text(val(data.Value));

        svg         .selectAll('text.total-name')
                    .text(val(data.Name));

        clusters
                    .attr('class', function(d, i) { 
                        if(d[1] === 0){ return 'cluster empty'}
                        else {return 'cluster'}
                    })
                    .attr("r", function (d, i) { return r(d[1]); });

        clusterValues   
                    .text(function(d) { return d[1] });

        clusterNames    
                    .text(function(d, i) { return d[0] });


        $(window).resize(function() {
          var w = $('.cluster-chart').width(); //make this more generic
          svg.attr("width", w);
          svg.attr("height", w * height / width);
        });

    });


}

chart.width = function(_) {
    if (!arguments.length) return width;
    width = _;
    return chart;
};

chart.onClusterClick = function(_) {
    if (!arguments.length) return onClusterClick;
    onClusterClick = _;
    return chart;
};

return chart;
}

1 个答案:

答案 0 :(得分:1)

我在所有相关的svg元素(包括svg本身)中应用了enter / update / exit模式。以下是一个示例细分:

var clusterValues = svg.selectAll("text.cluster-value")
    .data(data.Clusters,function(d){ return d[1];});

clusterValues.exit().remove();

clusterValues
    .enter().append('text')
    .attr('class', 'cluster-value');
...

这是一个包含所有部分的完整FIDDLE

注意:我尝试尽可能少地触摸您的代码,因为您已经仔细考虑应用可重用的方法。这就是为什么输入/更新/退出模式在总圆(和文本)和其他圆(和文本)之间有点不同的原因。我可能已经使用svg:g元素对每个圆圈和相关文本进行分组。