d3JS图表无法正确渲染

时间:2015-06-26 22:03:08

标签: javascript d3.js

鉴于以下代码。我能够创建一个条形图,由随机数据填充并使其适当显示。不幸的是,对我来说,我无法正确呈现图表的update

  

轴似乎相应地更新,但图形的条形图没有显示在相对于其轴的正确位置。下面有一个JSFiddle来说明问题。您会注意到在不同位置沿x轴呈现的条形图。

var Chart = (function (){
    function Chart(options){
        this.chart = d3.select(options.el);
        this.margin = options.dimensions.margin;
        this.height = options.dimensions.height;
        this.width = options.dimensions.width;
        this.data = options.data;
    }
    return Chart;
}());
var BarChart = (function(){
    function BarChart(options){
        Chart.call(this, options);

        this.xscale;
        this.vguidescale;
        this.vaxis;
        this.haxis;

        this.height = this.height - this.margin.top - this.margin.bottom;
        this.width = this.width - this.margin.left - this.margin.right;
        this.barcolors = createBarColors(options.barcolors);
        this.verticalTickCount = options.verticalTickCount;
        this.horizontalTickRangeCount = options.horizontalTickRangeCount || 10;
        this.chart = this.chart
            .append('svg')
                .attr('class', 'metric-chart')
                .attr('width', this.width + this.margin.left + this.margin.right)
                .attr('height', this.height + this.margin.top + this.margin.bottom)
                .append('g')
                    .attr('class', 'metric-chart__chart-bar')
                    .attr('transform', 'translate('+ this.margin.left +', '+ this.margin.top +')');
        this.vguide = d3.select('svg').append('g');
        this.hguide = d3.select('svg').append('g');
    }

    BarChart.prototype = Chart.prototype;
    BarChart.prototype.update = function(data) {
        var that = this;

        this.xscale = _getAxisScaleX(data, this.width);
        this.yscale = _getAxisScaleY(data, this.height);

        this.bars = this.chart.selectAll('rect')
            .data(d3.values(data))
            .order();

        this.bars.remove()
        this.bars.enter()
            .append('rect')
                .style('fill', function(d,i) { return that.barcolors[i % that.barcolors.length]; })
                .attr('width', that.xscale.rangeBand())
                .attr('x', function(d,i) {
                    return that.xscale(i);
                })
                .attr('height', 0)
                .attr('y', this.height)
                .transition()
                .attr('height', function(d) { return that.yscale(d); })
                .attr('y', function(d) {
                    return that.height - that.yscale(d);
                })
                .duration(1500)
                .ease('elastic');

        this.bars.exit().transition().attr('height', 0).remove();

        this.vguidescale = _getVerticalGuideScale(data, this.height);
        this.haxis = d3.svg.axis()
            .scale(this.xscale)
            .orient('bottom')
            .tickValues(this.xscale.domain().filter(function(d, i) {
                var remainder = i % that.horizontalTickRangeCount;
                return remainder === 0;
            }));
        this.vaxis = d3.svg.axis()
            .scale(this.vguidescale)
            .orient('left')
            .ticks(this.verticalTickCount);

        this.vguide
            .attr('transform', 'translate(' + this.margin.left + ', ' + this.margin.top + ')')
            .attr('class', 'metric-chart__vert-guide');
        this.hguide
            .attr('transform', 'translate(' + this.margin.left + ', ' + (this.height + this.margin.top) + ')')
            .attr('class', 'metric-chart__horz-guide');

        this.vaxis(this.vguide);
        this.haxis(this.hguide);
    };

    function _getAxisScaleX (data, width) {
        return d3.scale.ordinal()
            .domain(d3.range(0, data.length))
            .rangeBands([0, width], 0.2);
    }

    function _getAxisScaleY (data, height) {
        return d3.scale.linear()
            .domain([0, d3.max(data)])
            .range([0, height]);
    }

    function _getVerticalGuideScale (data, height) {
        return d3.scale.linear()
            .domain([0, d3.max(data)])
            .range([height, 0]);
    }

    function createBarColors(colors){
        return colors || ['#363636','#565656', '#868686'];
    }

    return BarChart;
}());

用法:

var chartDimensions = {
    margin : { top: 10, right: 10, bottom: 25, left: 25 },
    height : 200,
    width : 500
}, chartoptions;

chartoptions = {
    el : '.graph',
    data: genRandomArray(0, 50),
    dimensions : chartDimensions,
    verticalTickCount : 10,
    horizontalTickRangeCount : 5
};

/* generate bar chart and update on an interval ... */
var chart = new BarChart(chartoptions);
(function updateChart(){
    chart.update(genRandomArray(0, genrandom(10, 90)));
    setTimeout(updateChart, 4000);
}());

发行演示:

http://jsfiddle.net/h8ztvmq4/

1 个答案:

答案 0 :(得分:1)

我认为你略微误用了D3选择概念:当你this.bars = ... .data() ...时,你最终得到了更新选择,即之前改变了价值的所有元素。在该选择中,您调用了.remove(),暗示应删除已更改的元素。这显然不是你想要的。之后,您正确处理enter()exit()选项,添加新元素并删除不再需要的旧元素。如果您在this.bars.remove()调用之前仅删除了对this.bars.enter()的第一次调用,它看起来更像您可能想要的内容:尾部(更改条形数量)按预期工作。只是,头部仍未正确更新。为此,您需要使用enter()调用之前的更新选择来修改条形的相应属性(例如xy,{{1} }),也许是使用过渡。

Here's an updated fiddle.它仍然看起来不太完美,因为每当条形数量发生变化时你都会改变x尺度。因此,更换条需要水平移动,但是现有的条不能一致地移动,因此这两个选择会短暂地重叠。您应该考虑在单独的过渡中更新轴。