Sunday, August 5, 2012

HTML5 Charting Tool Performance Comparison

Akram El Assas has written up a performance comparison of dygraphs and a few other HTML5 charting libraries over at CodeProject.

dygraphs has always been written with large data sets in mind, and so it does quite well on these benchmarks. The one million point benchmark is particularly good:

dygraphs was the only library to complete the 1M point benchmark on 3/5 browsers, and was 6x faster than the competition on one of the others. There are a few tricks that we use to keep dygraphs running fast on large data sets. These are mostly under-the-hood, but they'll be great fodder for a future blog post.


Wednesday, August 1, 2012

Introducing Custom Plotters

Once you get your chart displaying with dygraphs, you'll want to customize its appearance.

dygraphs includes a plethora of options for doing simple customization, e.g. colors, strokeWidth, drawPoints, stackedGraph and fillGraph. You can see examples of all of these in use from the links off the options reference, or you can play around with them yourself using the experimental palette.

But what if you want to do something really crazy? To fundamentally change the way that data series are displayed? We've recently added a new plotter option which lets you customize to your heart's content.

The standard plotter just draws lines from point to point. While it's a bit more complex in reality, in essence, it's just this:

g = new Dygraph(div, data, {
  plotter: function(e) {
    var ctx = e.drawingContext;
    ctx.beginPath();
    ctx.moveTo(e.points[0].canvasx, e.points[0].canvasy);
    for (var i = 1; i < e.points.length; i++) {
      var p = e.points[i];
      ctx.lineTo(p.canvasx, p.canvasy);
    }
    ctx.stroke();
  }
});
(View/Edit in jsFiddle)

The plotter is called once for each series that needs to be drawn. Its one parameter is a dictionary of useful data about the series and the dygraph. The essential properties are the ones which we use here, namely:

  • drawingContext: a <canvas> drawing context on which you should render this series.
  • points: an array containing the individual points to draw. Each entry has a canvasx and canvasy attribute which tells you where to draw the point.

dygraphs will set up the drawing context for you as best it can. There's no need for you to set the stoke width or color for each series unless you want to do something unusual.

Other properties passed to the plotter include:

  • setName: the name of the series being drawn, e.g. "Y2"
  • color: the color of the series being drawn (you won't normally need this, see above)
  • strokeWidth: the width of the line being drawn (again, you won't normally need this, see above)
  • dygraph: A reference to current dygraph object (useful for calling getOption() and friends)
  • plotArea: Contains x, y, w and h properties which define the plotting rectangle.

As a simple example of what a custom plotter can do, let's make dygraphs draw bar charts instead of line charts.

Here's a bar chart plotter:

function barChartPlotter(e) {
  var ctx = e.drawingContext;
  var points = e.points;
  var y_bottom = e.dygraph.toDomYCoord(0);  // see http://dygraphs.com/jsdoc/symbols/Dygraph.html#toDomYCoord

  // This should really be based on the minimum gap
  var bar_width = 2/3 * (points[1].canvasx - points[0].canvasx);
  ctx.fillStyle = e.color;

  // Do the actual plotting.
  for (var i = 0; i < points.length; i++) {
    var p = points[i];
    var center_x = p.canvasx;  // center of the bar

    ctx.fillRect(center_x - bar_width / 2, p.canvasy,
        bar_width, y_bottom - p.canvasy);
    ctx.strokeRect(center_x - bar_width / 2, p.canvasy,
        bar_width, y_bottom - p.canvasy);
  }
}

And a dygraph that uses it:

g = new Dygraph(div, data, {
  plotter: barChartPlotter,
  dateWindow: [0, 9]  // avoid clipping first and last bars
});

and here's what the output looks like:

(View/Edit in jsFiddle)

Pretty easy! And since this is dygraphs, you get nice things like (animated) zooming, panning and hover labels for free. Try it out by clicking and dragging to zoom, or holding down shift and dragging to pan.

You can mix and match different plotters by setting the plotter option on a per-series basis. Here's how you might combine a line and bar chart (aka a Pareto chart):

g = new Dygraph(div, data, {
  "Late Arrivals": {
    plotter: barChartPlotter
  },
});
(View/Edit in jsFiddle)

This is admittedly a bit simplified: a real Pareto chart would list causes on the x-axis (instead of numbers) and would use two y-axes. In a future post, we'll try to recreate this Wikipedia Pareto chart more precisely using a valueFormatter and a secondary y-axis.

In the mean time, check out the plotters demo for some examples of how to use this exciting new tool.