Easy Vector Graphics with the Raphaël JavaScript Library

Raphaël is a small JavaScript library written by Dmitry Baranovskiy of Atlassian, that allows you to create and manipulate vector graphics in your web pages. It’s amazingly simple to use and is cross-browser compatible; supporting Internet Explorer 6.0+, Safari 3.0+, Firefox 3.0+, and Opera 9.5+. Internally Raphaël uses VML in IE and SVG in the other browsers.

Now, demos involving circles and squares are fine, but I wanted to create an example that demonstrated a legitimate, practical use of vector graphics. So how about real-time statistics measurement? Here’s a screenshot of my Current Sprocket Usage line graph that plots real-time “sprocket” usage levels. Best of all, it was a snap to make.

The HTML is simple; we just need a heading and container to hold our canvas — a div element:

<h1>Current Sprocket Usage: <span id="readout"></span></h1>
<div id="graph"></div>

To start we have to generate a new graphics canvas. I always like to place all my code within an object definition in order to create a separate namespace, so we’ll start with the following code:

var SpGraph = {
  init : function(){
    SpGraph.graph = Raphael("graph", 400, 200);
    SpGraph.graph.rect(0, 0, 390, 110, 10).attr("fill", "#000");
  }
}

window.onload = function () {
  SpGraph.init();
};

Using the window.onload event we call our SpGraph.init method. Within this method we create our canvas using Raphael("graph", 400, 200). The first argument is the ID of our container element, the other two represent width and height. We store the returned canvas object in our SpGraph.graph property. With the next line we create a rectangle and set some attributes:

SpGraph.graph.rect(0, 0, 390, 110, 10).attr("fill", "#000");

The rect method allows us to draw a rectangle specifying the x coordinate, y coordinate, width, height, and optionally a corner radius. Notice that we’ve also chained a call to the attr method to set the fill color. All Raphaël graphic objects support the attr method and there’s a range of attributes you can set. Raphaël supports chaining all its methods, which we will take advantage of soon. Our effort so far has resulted in this lovely black rectangle with rounded corners.

Now lets add stripes! To do this we add the following loop to the SpGraph.init method:

for(var x = 10; x < 110; x += 10) {
  var c = (x > 10) ? "#333" : "#f00";
  SpGraph.graph.path({stroke: c}).moveTo(0, x).lineTo(390,x);
}

The loop executes 10 times drawing a line each time; a red line for the first one and a gray line for the others. The Raphaël path method initializes the path mode of drawing, returning a path object. It doesn’t actually draw anything itself; you have to use the path object methods, which are chainable. The moveTo method moves the drawing cursor to the specified x and y coordinates and the lineTo method draws a line from the cursor point to the point specified. The result is the stripey background below:

So now we have to draw the actual graph line. The vertical axis (represented by the stripes) is the percentage usage level. The horizontal axis will represent time in 10 pixel increments. In the real world each update of the graph would be obtained via an Ajax call, say every 5 seconds, but here I just create random values and update the graph every second. Once again, we use the path method to draw a 5 pixel wide line.

We initialise the path and store the reference to it in the SpGraph.path property like so:

SpGraph.path = SpGraph.graph.path({
    stroke: "#0f0",
    "stroke-width": 5, 
    "fill-opacity": 0
}).moveTo(20, 110);

Every update, we extend the line using the lineTo method like so:

SpGraph.path.lineTo(20+SpGraph.updates*10, 110-perf);
perf is a random value between 0 and 100. The SpGraph.updates property is a simple counter that allows us to control how many updates before the line is reset. The counter value is also used to plot the location of the line on the horizontal axis. After 35 updates the line is reset by removing it, using the SpGraph.path.remove method, and starting a new one.

So the whole script looks like this:

var SpGraph = {
  init : function(){
    SpGraph.graph = Raphael("graph", 400, 200);
    SpGraph.graph.rect(0, 0, 390, 110, 10).attr("fill", "#000");

    for(var x = 10; x < 110; x += 10) {
      var c = (x > 10) ? "#333" : "#f00";
      SpGraph.graph.path({stroke: c}).moveTo(0, x).lineTo(390,x);
    }
    SpGraph.startPath();
    SpGraph.updateGraph();
  },
  startPath : function() {
    if(SpGraph.path) {
      SpGraph.path.remove();
    }
    SpGraph.path = SpGraph.graph.path({
        stroke: "#0f0",
        "stroke-width": 5, 
        "fill-opacity": 0
    }).moveTo(20, 110);
  },
  updateGraph : function() {
    if(SpGraph.updates++ < 36) {
      // imagine this value comes from an ajax request
      var perf = Math.floor(Math.random() * 100);
      SpGraph.path.lineTo(20+SpGraph.updates*10, 110-perf);
      document.getElementById('readout').innerHTML = perf+'%';
    } else {
      SpGraph.updates = 0;
      SpGraph.startPath();
    }
    SpGraph.timer = setTimeout("SpGraph.updateGraph();",1000);
  },
  updates : 0
}
window.onload = function () {
  SpGraph.init();
};

Don't forget to see it working in the demo. OK, so maybe a sprocket usage graph isn't exactly the legitimate, practical example I promised, but at least you got a look at what you can achieve with Raphaël with only a little effort. The documentation on the site isn't complete, but it's not too difficult to work out anyway. Why don't you have a go yourself? Quick, Simple, cross-browser compatible, vector graphics on the web has never been easier.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.brothercake.com/ brothercake

    Does the data have to be written in script, or does the library have the capability to extract its data from existing HTML? If it’s pure JS there’s no fallback content for assistive devices to interpret, or for non-scrip users to see, therefore no legitimate way of using it at all (without adding that layer yourself).

  • http://tetlaw.id.au Andrew Tetlaw

    Well the library is purely for the creation of vector graphics, so I don’t think it’s the responsibility of this library. In the demo, I’ve updated the heading of the graph, a h1 element, with the current performance value each time the graph updates. No reason why the heading couldn’t have the initial value on page load.

    But, what’s the best way to update page content via Ajax and remain accessible?

    I guess, you could also have a manual refresh control, that grabs the latest perf data (since last update) and then produces a static graph from the data in the HTML.

  • http://www.brothercake.com/ brothercake

    There’s no way to update page content via Ajax and remain accessible. Not if you’re being realistic.

    But then I suppose it depends on your benchmark. It depends whether you count JS support as an accessibility issue (I do; many don’t), and whether you can presume that a screenreader user is using the latest and greatest version (which may support ARIA, where older ones don’t).

    ARIA = Accessible Rich Internet Applications (to me, an oxymoron if ever there was one; but I’m not gonna get into that here — have a look at some of this for more:

    http://www.alistapart.com/articles/waiaria (thinks it’s cool)
    http://www.sitepoint.com/article/accessible-javascript/ (not at all convinced .. actually I wrote that one, just so you know where the bias is coming from ;))

  • http://www.brothercake.com/ brothercake

    Oh I meant this one actually, but the other one is still interesting — http://www.sitepoint.com/article/ajax-screenreaders-work/

  • Anonymous

    How about a more realistic demo?

    This example really needs an abstraction layer, so much so that it is almost impossible to digest how good or bad this thing is as a graphics library.

  • Brian Lowe

    I’m not sure whether this is a comment about the library or browser support for the library but…

    The demo works seamlessly in FireFox, Safari and Google’s Chrome, but IE8 reports invalid JavaScript unless I tell it to work in “compatibility mode” (emulating IE7).

    It appears IE8’s default “standards mode” is still out of step with the other browsers? :(