【问题标题】:Updating D3 chart resulting in duplicate charts更新 D3 图表导致图表重复
【发布时间】:2013-04-03 05:35:24
【问题描述】:

单击按钮后,我在尝试将 JSON 数据更新到 D3 直方图时遇到了麻烦。

虽然我解决了按钮单击更新问题,但 D3 javascript 现在每次按钮单击都会附加一个图表,从而导致图表重复,而不是简单地更新数据。

我知道点击事件每次触发时都会在 D3 代码中调用append(),但我该如何解决这个问题,所以每次点击只有一个图表更新数据?

console.log('chart.js loaded');

$(document).ready(function() {

  var vimeoVideoId = $('p#vimeoVideoId').text();
  var api = 'http://localhost:3001/videos/' + vimeoVideoId + '/json';

  function initData() {
    $('#all-notes').click(function() {
      getData();
    });
  }

  function getData() {
    $.getJSON(api, draw);
  }

  function draw(json) {

    data = json;
    var duration = data.duration;

    var timeToSec = function(data) {
      notes = [];
      // convert min:sec to seconds
      for(i=0; i < data.notes.length; i++) {
        var min_sec = data.notes[i].timecode;
        var tt = min_sec.split(':');
        var seconds = tt[0]*60+tt[1]*1;
        notes.push(seconds);
      }
      return notes;
    };

    noteTimes = timeToSec(data);
    console.log(noteTimes);

    // Formatters for counts and times (converting numbers to Dates).
    var formatCount = d3.format(",.0f"),
        formatTime = d3.time.format("%H:%M"),
        formatMinutes = function(d) { return formatTime(new Date(2012, 0, 1, 0, d)); };

    var margin = {top: 10, right: 20, bottom: 30, left: 20},
        width = 550;
        height = 285;

    var x = d3.scale.linear()
        .domain([0, duration])
        // .domain([0, d3.max(noteTimes)])
        .range([0, width]);

    // Generate a histogram using twenty uniformly-spaced bins.
    var data = d3.layout.histogram()
        .bins(x.ticks(50))
        (noteTimes);

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

    var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom")
        .tickFormat(formatMinutes);

    var svg = d3.select("#chartSet").append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
      .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var bar = svg.selectAll(".bar")
        .data(data)
      .enter().append("g")
        .attr("class", "bar")
        .attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; });

    bar.append("rect")
        .attr("x", 1)
        .attr("width", x(data[0].dx) - 1)
        .attr("height", function(d) { return height - y(d.y); });

    bar.append("text")
        .attr("dy", ".75em")
        .attr("y", 6)
        .attr("x", x(data[0].dx) / 2)
        .attr("text-anchor", "middle")
        .text(function(d) { return formatCount(d.y); });

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);
  }

  initData();

});

【问题讨论】:

    标签: jquery d3.js getjson


    【解决方案1】:

    要处理创建和更新,您必须重新组织 draw 函数的编写方式。

    function draw(json) {
    
       // Initialization (few wasted CPU cycles)
       // ...
    
       // Update hook
       var svg = d3.select("#chartSet").data([data]);
    
       // Enter hook
       var svgEnter = svg.enter();
    
       // Enter cycle
       svgEnter.append("svg")
          .append("g")
          .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
        // Update cycle
        svg
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom);
    
        // Exit cycle (not really needed here)
        svg.exit().remove();
    
        // Update hook
        var bar = svg.selectAll(".bar")
            .data(data)
    
        // Enter hook
        var barEnter = bar.enter();
    
        // Enter cycle
        var barG_Enter = barEnter
                            .append("g")
                            .attr("class", "bar")
        barG_Enter
            .append("rect")
            .attr("x", 1);
    
        barG_Enter
            .append("text")
            .attr("dy", ".75em")
            .attr("y", 6)
            .attr("text-anchor", "middle");
    
        // Update cycle
        bar.attr("transform", function(d) { 
            return "translate(" + x(d.x) + "," + y(d.y) + ")"; });
    
        bar.select("rect")
            .attr("width", x(data[0].dx) - 1)
            .attr("height", function(d) { return height - y(d.y); });
    
        bar.select("text")
            .attr("x", x(data[0].dx) / 2)
            .text(function(d) { return formatCount(d.y); });
    
        // Exit cycle
        bar.exit().remove();
    
        // Enter cycle
        svgEnter.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + height + ")")
            .call(xAxis);
    
        // Update cycle
        svg.select('g.x.axis')
            .call(xAxis);
    }
    

    这个classical enter-update-exit 模式在this article on making reusable graphs 中得到了改进。这个答案很大程度上借鉴了这种模式。

    通过使用闭包的稍微好一点的实现,您将能够节省每次初始化时浪费的几个 CPU 周期。

    【讨论】:

      【解决方案2】:

      您可以在单击按钮时调用的函数之外创建 SVG,或者检查它是否存在,如果不存在则仅添加它。也就是说,像

      var svg = d3.select("svg");
      if(svg.empty()) {
          // code for appending svg
      }
      

      同样的事情也适用于函数末尾附加 x 轴的代码块。对于条形图,除了输入选择之外,您需要做的就是处理更新和退出选择,即设置尺寸并可能根据新数据删除元素。

      【讨论】:

        猜你喜欢
        • 2015-11-14
        • 1970-01-01
        • 2016-03-13
        • 1970-01-01
        • 1970-01-01
        • 2015-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多