d3.js - 如何更新图表数据?

时间:2014-04-21 22:18:56

标签: javascript d3.js transition

我学习D3.js之旅的另一步......

我有一个简单的可重复使用的图表,我称之为“聚类图表”,而我正试图简单地更新一些值而不添加或减去任何元素。

这里有一个小提琴:http://jsfiddle.net/rolfsf/8bXAN/

图表脚本如下所示:

    function clusterChart() {
    var width = 500,
        radiusAll = 90,
        maxRadius = radiusAll - 5,
        padding = 8, 
        height = 3 * (radiusAll*2 + padding),
        startAngle = Math.PI / 2,
        val = function(d) { return d; },
        onTotalMouseOver = null,
        onTotalClick = null,
        onClusterMouseOver = null,
        onClusterClick = null;

    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;


            //  Remove svg, if already exist
            d3.select(this).select('svg').remove();

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

            //  Add svg element
            var svg = d3.select(this).append('svg')
                .attr('class', 'cluster-chart')
                .attr("viewBox", "0 0 " + width + " " + height )
                .attr("preserveAspectRatio", "xMidYMin meet")
                .attr('width', width)
                .attr('height', height);

            //  Total group value
            var totalCluster = svg.append('g')
                .attr('class', 'total-cluster');

            totalCluster.append('circle')
                .attr('cx', cx)
                .attr('cy', cy)
                .attr('r', radiusAll)
                .on('mouseover', onTotalMouseOver)
                .on('click', onTotalClick);

            totalCluster.append('text')
                .attr('class', 'value')
                .attr('x', cx)
                .attr('y', cy + 4)
                .text(val(data.Value));

            totalCluster.append('text')
                .attr('class', 'group-name')
                .attr('x', cx)
                .attr('y', cy + 16)
                .text(val(data.Name));

            //  Clusters values
            var cluster =  svg.selectAll('g.cluster')
                .data(data.Clusters)
                .enter().append('g')
                .attr('class', function(d, i) { 
                    if(d[1] === 0){ return 'cluster empty'}
                    else {return 'cluster'}
                });

            cluster.append('circle')
                .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", function (d, i) { return r(d[1]); })
                .on('mouseover', function(d, i, j) {
                    //do something
                    if (onClusterMouseOver != null) onClusterMouseOver(d, i, j);
                })
                .on('mouseout', function() {
                    //do something
                })
                .on('click', function(d, i){ onClusterClick(d); });  

            cluster.append('text')
                .attr('class', 'value')
                .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; })
                .text(function(d) { return val(d[1]); });

            cluster.append('text')
                .attr('class', 'group-name')
                .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; })
                .text(function(d) { return val(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.height = function(_) {
        if (!arguments.length) return height;
        height = _;
        return chart;
    };

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

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

    return chart;
 }

我将图表称为:

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

最终,数据将来自json文件,但现在它已经存在于函数中。

假设我想在单击#doSomething按钮时更新值,更新的数据如下所示:

.datum({
    Name: 'Total Widgets',
    Value: 150,
    Clusters: [
        ['Other', 25],
        ['FooBars', 25],
        ['Foos', 25],
        ['Bars', 75],
        ['BarFoos', 0]
    ]
})

如何更新这些数据,并在半径变化时进行了很好的转换?

更新:好的,我意识到一行几乎消除了我“更新”图表的能力。

//  Remove svg, if already exist
d3.select(this).select('svg').remove();

相反,我应该做类似的事情:

//  Add svg element
var svg = d3.select(this).selectAll('svg')

所以我不会删除整个图表。但即使如此,我也不知道如何使用新数据进行更新。

任何线索?

0 个答案:

没有答案