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

Updating data in a simple reusable D3 chart

在书 "Data Visualization with 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();
}

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

非常感谢!

完整的代码是here,但是下面我复制了最重要的部分:

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() 函数自动拾取。这是有效的,因为闭包存储了一个 reference 到系列(已修改)而不是实际值。

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

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