JavaScript - - By Andrew Tetlaw

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.

Sponsors