在简单的可重用 D3 图表中更新数据

Updating data in a simple reusable D3 chart

本文关键字:更新 数据 D3 简单      更新时间:2023-09-26

在"D3 Cookbook的数据可视化"一书(第8章)中出现了一个作者绘制了两条线的例子。基本上,数据是随机创建的:

var numberOfSeries = 2,
    numberOfDataPoint = 11,
    data = [];
for (var i = 0; i < numberOfSeries; ++i)
    data.push(d3.range(numberOfDataPoint).map(function (i) {
        return {x: i, y: randomData()};
    }));

然后,作者创建图表的实例并定义刻度的域:

var chart = lineChart()
        .x(d3.scale.linear().domain([0, 10]))
        .y(d3.scale.linear().domain([0, 10]));

之后,使用 addSeries() 方法引入数据:

data.forEach(function (series) {
    chart.addSeries(series);
});

最后,调用 render() 方法呈现图表:

chart.render();

此外,该图有一个Update的屁股,允许通过调用此函数更新显示的数据:

function update() {
    for (var i = 0; i < data.length; ++i) {
        var series = data[i];
        series.length = 0;
        for (var j = 0; j < numberOfDataPoint; ++j)
            series.push({x: j, y: randomData()});
    }
    chart.render();
}

我的问题是,在按下Update按钮(即在update()函数中)后,如果不再次调用 addSeries() 方法,我无法理解数据如何在图中更新。数据内部存储在变量_data中,可以用方法addSeries()进行修改。有什么想法吗?

非常感谢!

完整的代码在这里,但下面我复制了最重要的部分:

function lineChart() { // <-1A
    var _chart = {};
    var _width = 600, _height = 300, // <-1B
            _margins = {top: 30, left: 30, right: 30, bottom: 30},
            _x, _y,
            _data = [],
            _colors = d3.scale.category10(),
            _svg,
            _bodyG,
            _line;
    _chart.render = function () { // <-2A
        if (!_svg) {
            _svg = d3.select("body").append("svg") // <-2B
                    .attr("height", _height)
                    .attr("width", _width);
            renderAxes(_svg);
            defineBodyClip(_svg);
        }
        renderBody(_svg);
    };
    // Axis rendering functions ...

    function renderBody(svg) { // <-2D
        if (!_bodyG)
            _bodyG = svg.append("g")
                    .attr("class", "body")
                    .attr("transform", "translate(" 
                        + xStart() + "," 
                        + yEnd() + ")") // <-2E
                    .attr("clip-path", "url(#body-clip)");        
        renderLines();
    }
    function renderLines() {
        _line = d3.svg.line() //<-4A
                        .x(function (d) { return _x(d.x); })
                        .y(function (d) { return _y(d.y); });
        _bodyG.selectAll("path.line")
                    .data(_data)
                .enter() //<-4B
                .append("path")                
                .style("stroke", function (d, i) { 
                    return _colors(i); //<-4C
                })
                .attr("class", "line");
        _bodyG.selectAll("path.line")
                .data(_data)
                .transition() //<-4D
                .attr("d", function (d) { return _line(d); });
    }
    //Some getter/setters functions and other stuff
    _chart.addSeries = function (series) { // <-1D
        _data.push(series);
        return _chart;
    };
    return _chart; // <-1E
}
function randomData() {
    return Math.random() * 9;
}
function update() {
    for (var i = 0; i < data.length; ++i) {
        var series = data[i];
        series.length = 0;
        for (var j = 0; j < numberOfDataPoint; ++j)
            series.push({x: j, y: randomData()});
    }
    chart.render();
}
var numberOfSeries = 2,
    numberOfDataPoint = 11,
    data = [];
for (var i = 0; i < numberOfSeries; ++i)
    data.push(d3.range(numberOfDataPoint).map(function (i) {
        return {x: i, y: randomData()};
    }));
var chart = lineChart()
        .x(d3.scale.linear().domain([0, 10]))
        .y(d3.scale.linear().domain([0, 10]));
data.forEach(function (series) {
    chart.addSeries(series);
});
chart.render();

update() 函数正在就地修改每个系列,由render()函数自动拾取。这是有效的,因为闭包存储对序列(已修改)的引用,而不是实际值。

发生的情况与修改全局变量时相同。addSeries()函数添加一个新系列,但在这种特殊情况下,您不是添加任何新系列,而是修改现有系列。

也就是说,我不认为代码设计得特别好。