I was recently looking through client-side alternatives to some of the server-side graphing solutions and was recommended to try flot.  Flot is a jQuery plugin that allows some quite impressive graphing straight out-the-box.

Flot also supports plugins (as of version 0.6) which means that, if you’re willing to look around, there’re a number of plugins that extend the default functionality. I’ve found flot pie charts and flot gauge charts as two examples. Unfortunately I’ve found finding these plugins rather frustrating – most of them are in the forums, but finding the latest versions can be time-consuming.

One main thing I found missing was the ability to add custom markers.  Googling around I found on elctech talking about the modifications they made to flot to support custom marker drawing.  This idea was the basis of this article.  The issue I had with this was that the blog post stopped at “if you want custom markers, you can draw them here”.  My plan was to extend that blog post.

My main concerns were:

  • Make as few changes to the flot library as possible (I added three lines in the end)
  • Make the changes flexible so that I could customise the rendering on each call
  • Allow myself to fall back to the default rendering method if required.

I decided that the approach to take was to optionally provide an additional rendering function to the plotPoints function.  This rendering function could be missing (the default) and the library would function as it currently does, the rendering function could render a custom graphic, or the rendering function could allow control to pass back into the default renderer.

With this approach, my first job was to add a property named “renderFunction” with a default of null around line 89 in jquery.flot.js:

The next step was to modify the plotPoints function to call this function if it was set.  If the render function is set then it must return either true or false. If it returns true, the default point rendering is bypassed.  If the render function returns false, the default point rendering is applied.


Lastly, I had to actually write the render function.  In my example I override the point rendering if the value is less than 3.5 (and return true).  Values over 3.5 are handed back to the original rendering routine (by returning false).

The only issue I encountered was that the images MUST be loaded prior to passing them through to canvas.drawImage.  If the image isn’t properly loaded then the canvas object will throw an error, at least in FireFox. To resolve this, I created a function that ensures the onload event of each image has run prior to attempting to render the graph.  The full code is below.  Please note that the value generation was lifted from an example within the flot code, so credit where it’s due.

function loadImages()
{
 var totalImages = arguments.length-1;
 var loadedImages = 0;
 var fn = arguments[0];
 for(var i=1; i<arguments.length; i++)
 {
  var $img = $("<img />");
  $img.load(function()
  {
   if((++loadedImages) == totalImages)
    fn();
  });
  $img.attr("src", arguments[i]);
 }
}
$(document).ready(
function() {
    loadImages(function() {
        var d5 = [];
        for (var i = 0; i < 14; i += 0.5)
            d5.push([i, Math.sqrt(i)]);
        var g = new Image();
        g.src = "bullet_green.png";
        var a = new Image();
        a.src = "bullet_yellow.png";
        var r = new Image();
        r.src = "bullet_red.png";
        $.plot($("#placeholder1"), [
        {
            data: d5,
            lines: { show: true },
            points: {
                show: true,
                // By providing a new render function we can customise
                // the point rendering.
                renderFunction: function(ctx, x, y, axisx, axisy) {
                    var i = null;
                    if (y < 1.5)
                        i = g
                    else if (y < 3)
                        i = a
                    else if (y < 3.5)
                        i = r
                    // If we don't have a custom marker image
                    // (e.g. y >= 3.5), use the default pointer rendering.
                    if (i == null) { return false; }
                    // Otherwise render the image.
                    ctx.drawImage(i, axisx.p2c(x) - 8, axisy.p2c(y) - 8);
                    return true;
                }
            }
        }
    ]);
    }, "bullet_green.png", "bullet_yellow.png", "bullet_red.png");
});

Lastly, here’s a screenshot of the rendered graph.  Values below 1.5 are shown in green, between 1.5 and 3 in orange, between 3 and 3.5 in red, and above 3.5 are handled by the default rendering routine.

Let me finish off by adding a few caveats:

  1. Code given must be checked before you use it.  It’s example code only.
  2. I know the code style isn’t exactly consistent with flot’s – it’s mine.
  3. I should probably pass more data into the render function.  Specifically I’ve been toying with some reference as to which series we’re plotting.  At the moment it’s not a major issue for me as I only need it for a single series – perhaps this is an exercise for the reader.