The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

Advanced Rendering with Callbacks

Overview

By default, D3 visualizations created with r2d3 are monolithic scripts that do all of their work as a single computation (the body of the script). While this makes scripts straightforward to author by default, not all visualizations will confirm to this simple model. Rather, some visualizations will want to distinguish between the following activities:

  1. Code that performs one-time initialization for the visualization (e.g. creation of a D3 layout).

  2. Code that runs whenever the data underlying the visualization is changed.

  3. Code that runs whenever the visualizations container element is resized.

This article describes how you can use the r2d3 advanced rendering API to organize your visualization code to run in this more granular fashion.

onRender Callback

To distinguish between code that runs at initialization-time only and code that runs when data changes, organize your code so that the code which responds to data changes is contained within the r2d3.onRender() callback. For example:

// Initialization
svg
 .attr("font-family", "sans-serif")
 .attr("font-size", "8")
 .attr("text-anchor", "middle");
    
var pack = d3.pack()
  .size([width, height])
  .padding(1.5);
    
var format = d3.format(",d");
var color = d3.scaleOrdinal(d3.schemeCategory20c);

// Rendering
r2d3.onRender(function(data, svg, width, height, options) {
  var root = d3.hierarchy({children: data})
    .sum(function(d) { return d.value; })
    .each(function(d) {
      if (id = d.data.id) {
        var id, i = id.lastIndexOf(".");
        d.id = id;
        d.package = id.slice(0, i);
        d.class = id.slice(i + 1);
      }
    });

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

  node.append("circle")
      .attr("id", function(d) { return d.id; })
      .attr("r", function(d) { return d.r; })
      .style("fill", function(d) { return color(d.package); });

  node.append("clipPath")
      .attr("id", function(d) { return "clip-" + d.id; })
    .append("use")
      .attr("xlink:href", function(d) { return "#" + d.id; });

  node.append("text")
      .attr("clip-path", function(d) { return "url(#clip-" + d.id + ")"; })
    .selectAll("tspan")
    .data(function(d) { return d.class.split(/(?=[A-Z][^A-Z])/g); })
    .enter().append("tspan")
      .attr("x", 0)
      .attr("y", function(d, i, nodes) { return 13 + (i - nodes.length / 2 - 0.5) * 10; })
      .text(function(d) { return d; });

  node.append("title")
      .text(function(d) { return d.id + "\n" + format(d.value); });
});

onResize Callback

By default, when the element which contains your visualization is resized, r2d3 will call back your script to re-render the visualization from scratch using the new size. In some cases this might be acceptable, but in other cases it makes sense to have code which explicitly handles the resize in a more efficient fashion.

You can provide an explicit resize handler by implementing the r2d3.onResize() callback. For example, in a force directed D3 layout you might do this in the onResize() callback:

r2d3.onResize(function(width, height) {
  force.force("center", d3.forceCenter(width / 2, height / 2))
    .restart();
});

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.