Search code examples
amchartsamcharts4

amcharts4 Force Directed Tree not dynamically updating


I am trying to animate the construction of a Force Directed Tree in amcharts4. The tree draws just fine, but only after I sequentially added all the nodes, not 1 by 1 like I'd like. Not sure if I'm doing something wrong or whether the amcharts4 FDTs don't allow for this type of chart updating.

Here's my codepen:

  // https://www.amcharts.com/docs/v4/chart-types/force-directed/
am4core.useTheme(am4themes_animated);

// Create chart
var amchart = am4core.create("chartdiv", am4plugins_forceDirected.ForceDirectedTree);
// Create series
var series = amchart.series.push(new am4plugins_forceDirected.ForceDirectedSeries())
// Set data

// Set up data fields
series.dataFields.value = "value";
// series.dataFields.name = "name";
series.dataFields.children = "children";
series.dataFields.id = "name";
series.dataFields.linkWith = "link";
series.fontSize = 9;
series.minRadius = 40;
series.maxRadius = 70;
series.nodes.template.circle.fillOpacity = 0.9;
series.nodes.template.circle.filters.push(new am4core.DropShadowFilter());

function sleep(milliseconds) {
    var start = new Date().getTime();
    for (var i = 0; i < 1e7; i++) {
        if ((new Date().getTime() - start) > milliseconds){
            break;
        }
    }
}

var array = [ {'name': 'A', 'text': 'A', 'username': 'A', 'value': 0.5333333333333333, 'link': ['B', 'C']}, {'name': 'B', 'text': 'B', 'username': 'B', 'value': 0.0, 'link': []}, {'name': 'C', 'text': 'C', 'username': 'C', 'value': 0.0}];
var arrayLength = array.length;
for (var i = 0; i < arrayLength; i++) {
  console.log(array[i], true);
  amchart.addData(array[i]);
  amchart.invalidateData();
  sleep(2000);
}

Solution

  • AmCharts' actions are all asynchronous, so your sleep method isn't exactly the best approach as it's likely that the redrawing keeps getting interrupted to the point where it will just result in the final chart output.

    A better approach is to use the chart's datavalidated event and use that to determine when to add the next node. If you want a delay, wrap it in setTimeout instead:

    amchart.events.on('datavalidated', gradualFill);  
    function gradualFill() {
      if (array.length) {
        setTimeout(function() {
          amchart.addData(array.pop()); //or array.shift(), or however you want to order the nodes' visibility
        }, 1000)
      }
      else {
        amchart.events.off('datavalidated', gradualFill); //remove this event handler when you're done
      }
    }
    

    Demo below:

    // https://www.amcharts.com/docs/v4/chart-types/force-directed/
    am4core.useTheme(am4themes_animated);
    
    // Create chart
    var amchart = am4core.create("chartdiv", am4plugins_forceDirected.ForceDirectedTree);
    // Create series
    var series = amchart.series.push(new am4plugins_forceDirected.ForceDirectedSeries())
    // Set data
    
    // Set up data fields
    series.dataFields.value = "value";
    // series.dataFields.name = "name";
    series.dataFields.children = "children";
    series.dataFields.id = "name";
    series.dataFields.linkWith = "link";
    series.fontSize = 9;
    series.minRadius = 40;
    series.maxRadius = 70;
    series.nodes.template.circle.fillOpacity = 0.9;
    series.nodes.template.circle.filters.push(new am4core.DropShadowFilter());
    
    var array = [ {'name': 'A', 'text': 'A', 'username': 'A', 'value': 0.5333333333333333, 'link': ['B', 'C']}, {'name': 'B', 'text': 'B', 'username': 'B', 'value': 0.0, 'link': []}, {'name': 'C', 'text': 'C', 'username': 'C', 'value': 0.0}];
    
    amchart.events.on('datavalidated', gradualFill);  
    function gradualFill() {
      if (array.length) {
        setTimeout(function() {
          amchart.addData(array.pop()); //or shift, or however you want to order the nodes' visibility
        }, 1000)
      }
      else {
        amchart.events.off('datavalidated', gradualFill); //remove this event handler when you're done
      }
    }
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
    }
    
    #chartdiv {
      width: 100%;
      height: 400px;
    }
    <script src="//www.amcharts.com/lib/4/core.js"></script>
    <script src="//www.amcharts.com/lib/4/charts.js"></script>
    <script src="//www.amcharts.com/lib/4/themes/animated.js"></script>
    <script src="//www.amcharts.com/lib/4/plugins/forceDirected.js"></script>
    <div id="chartdiv" style="height:200px;"></div>