简体   繁体   中英

general update pattern D3 - repainting unchanged nodes

I am making a d3 chart based off of time series data. I would like to plot moving averages based off a time window specified by the user in real time. All lines will share the same dataset. I am implementing the general update pattern (I believe), however, when I change the dataset, the update doesn't occur unless I call my update function explicitly. This works, however, a node is created for each element in the array (creating a new DOM element over the old one) as opposed to just adding the updated element. I feel I am misunderstanding something fundamental about D3, or perhaps about javascript itself. Here is a minimal working example of my code:

In brief, my chart has a setInterval method that adds to the dataset and updates all the lines on the chart. Each line shares the chart data, but will transform the data in different ways.

window.onload = function() {
  let mychart = chart( '#chart' );
  let props1 = {
    'className': 'line-1',
    'Color': 'red',
    'y': function( d ) { return d + 25; }
  }

  let line1 = line( mychart, props1 );

  mychart.addPlot( line1 );
  mychart.stream();
}

//need to find best way to share data across all indicators 
var chart = function(svg_id) {
  let data = [ 0, 1, 2, 3, 4, 5];
  let plots = [];
  let svg = ( function(  ) {
    return d3.select( svg_id + '-container' ).append( 'svg' )
      .attr( 'height', 500 )
      .attr( 'width', 1000 )
      .attr( 'id', svg_id.replace('#', '' ))
  })();

  let xscale = d3.scaleLinear()
                  .domain( [0, 100] )
                  .range( [10, 990] )

  let addPlot = function( plot ) {
    plots.push( plot );
  }

  let stream = function() {
    setInterval( function() {
      data.push( Math.random(0, 1) * 100);
      for( var i = 0; i < plots.length; i++ ) {
        plots[i].update();
      }
    }, 1000);
  }
  return {
    'svg': svg,
    'xscale': xscale,
    'data': data,
    'addPlot': addPlot,
    'stream': stream,
  }

}

var line = function( chart, props ) {
  let data = chart.data;
  let xscale = chart.xscale;
  let svg = chart.svg;

  let buildLine = d3.line()
    .x( function(d, i ) { return xscale( i ) + i*1; })
    .y( function(d) { return props.y( d ); })

  let update = function() {
    let line = svg.selectAll( props.className ).data( data );

    line.enter()
      .append( 'path' )
      .attr( 'stroke', props.Color )
      .attr( 'stroke-width', 2 )
      .attr( 'class', props.className )
      .attr( 'fill', 'none' )
      .merge( line )
        .attr( 'd', buildLine( data ))

    line.exit().remove();
  }


  return {
    'update': update,
  }
}

I found plenty online about nodes not being added, but nothing about d3 repainting on every iteration, and adding nodes every iteration. Thank you.

I'm not sure, but perhaps you can take a look at this: Key functions

As second argument of .data( data ) you can provide a function which uniquely identifies your data points. Like .data( data, function(d) { return d.someUniqueId; })

D3 can use this key to update DOM elements instead of regenerating them, as I understand it correctly from Mike Bostock's post .

I solved this using this tutorial on d3 paths https://www.dashingd3js.com/svg-paths-and-d3js . Key point is selectAll is not appropriate for paths since a line is only one 'path' element, therefore append is all that is necessary. I am removing the line using jquery, so still a bit hacky as I'm sure D3 has a way to do this. But here is an updated init and update functions.

  let init = function() {
    svg.append( 'path' )
      .attr( 'd', buildLine( data ))
      .attr( 'stroke', props.Color )
      .attr( 'stroke-width', 2 )
      .attr( 'class', props.className )
      .attr( 'fill', 'none' )

  }



  let update = function( newdata ) {
    data.push( newdata );
    $( '.' + props.className ).remove();
    init();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM