A List Apart

Menu

SVG with a little help from Raphaël

by Published in Browsers, JavaScript29 Comments

Raphaël is a light-weight JavaScript library that renders dynamic SVG graphics including charts, graphs, vector-based animations and GUI components right into your web pages. Now, you’re probably thinking, I can already do this with jQuery, Google Charts, or even Flash! While this is true, Raphaël reveals new possibilities not currently available with these other technologies. Let’s learn how to create inline scalable vector images that work across browsers and degrade gracefully.

Open language for an open web

Issue № 310

The web is all about open standards and unencumbered technologies. Raphaël, which uses W3C standard SVG and an open source MIT License, fits the open web perfectly.

But, what about Internet Explorer 6? It doesn’t support SVG! That’s true, but IE6 does support its own VML (Vector Markup Language). (IE9 is slated to support SVG 1.1. Note that Raphaël relies on feature detection rather than user agent (UA) sniffing. IE7 and IE8 don’t know what to do with an SVG file, so Raphaël automatically switches modes for you.) Raphaël abstracts all that browser nonsense away so that you don’t have to deal with it. The same JavaScript code will create beautiful SVG or VML based on the browser’s capabilities. Write it once and it runs anywhere. By sidestepping Flash for vector graphics, you make your output available on more devices, including mobile WebKit browsers and iOS devices. Your fancy, interactive, scalable graphics will look beautiful at any resolution on any supported device.

JQuery

What about jQuery? There are plenty of graphics plugins. Why should I switch to Raphaël?

There are plenty of interesting jQuery graphics plugins, such as jqPlot, Flot, jQuery SVG, and others. With jQuery, you commit to 50+ KB for the minified library. Then, add the UI core, some plugins, and the extra libraries to get it working for IE. Byte for byte, jQuery packs a punch, but you get extra bits you don’t always need.

Raphaël is smaller overall for creating basic vector graphics. Watching your KB weight is something to consider when developing large scale applications, but so is using the best tool for the job.

It doesn’t have to be an either/or scenario. If you are already using jQuery, it is possible to drop in Raphaël without conflicts and mix and match the two. Many of these JavaScript libraries live in harmony with each other, which makes our lives as developers easier.

Dynamic Images

Before we had all these libraries, tools, and web services at our disposal, dynamically generated graphics were created on the server by Common Gateway Interface (CGI) scripts. How things have changed. Now we have access to services such as Google charts and others using Representational State Transfer (REST). Just specify the parameters to get a lovely graphic in return.

Embedding Google charts in an <img> element is a fast and flexible way to create a nice bar chart on the fly, but it does have its downsides. As an image element, you are left only with the alt attribute. You must use longdesc or a hacked image map to label regions. All the data is locked up in the pixels and not in the HTML. Static images are rendered at a certain size. This means that as you zoom in, the image becomes pixelated. As you begin to build professional workflows, you’ll run into problems with rasterized graphics such as GIF, JPEG, and PNG, which don’t scale up or down very well. SVG, on the other hand, is made of vectors, so it is resolution independent. Since the web is available in all shapes and sizes, and because data is more frequently transitioned on and off the web, graphics must retain their resolution from magazine quality to low-end 72dpi screen resolutions and everything in between.

Examples

Progressive Enhancement

Raphaël uses JavaScript to inject the SVG or VML into the HTML page. This means that your browser needs to have JavaScript support to see the resulting graphics. While you’d expect most people browsing your site in any modern browser to have JavaScript turned on as well as support SVG, your largest and most important customers don’t. Search engine bots, such as Google Bot, don’t render JavaScript. It doesn’t “see” your beautiful graphs. It sees an empty div with no content!

This is where progressive enhancement is key. The raw HTML data serves as your baseline. It might not look pretty as a table or list, but it is semantically correct and adds hooks for search engines to index your content. It is now possible to use the HTML as your data source when constructing the Raphaël graphic.

                       
Nutritional information for product X
Calories45%
Saturated Fats25%
Protein15%
Vitamin B10%
Vitamin C5%

First we create a table of values that add up to 100%. These are the values for each wedge in the pie chart. We can enhance the regular table if the browser supports it.


  <table id="nutritionFacts">
    <caption>Nutritional information for product X</caption>
    <tr>
      <th scope="row">Calories</th>
      <td>45%</td>
    </tr>
    <tr>
      <th scope="row">Saturated Fats</th>
      <td>25%</td>
    </tr>
    <tr>
      <th scope="row">Protein</th>
      <td>15%</td>
    </tr>
    <tr>
      <th scope="row">Vitamin B</th>
      <td>10%</td>
    </tr>
    <tr>
      <th scope="row">Vitamin C</th>
      <td>5%</td>
    </tr>
  </table>

Now that we have our baseline raw data, we can add a few external libraries. First we must include Raphaël.


  <script src="raphael.js" type="text/javascript" charset="utf-8"></script>

Then we’ll use Raphaël’s plugin system to include the Pie Charts plugin.


  <script src="pie.js" type="text/javascript" charset="utf-8"></script>

This gives us access to all the tools we need to convert the table into an SVG graphic with ease. To generate the pie chart, we must first create two arrays, one for the labels and a second one for the values.

var values = [],
  labels = [],
  nfTable = document.getElementById("nutritionFacts"),
  i = 0, 
  rows = nfTable.rows.length;for( ;i < rows; i++) {
  trRow = nfTable.getElementsByTagName("tr")<i>;
  labels.push(trRow.getElementsByTagName("th")[0].firstChild.nodeValue);
  values.push(parseInt(trRow.getElementsByTagName("td")[0]
  .firstChild.nodeValue, 10));
}nfTable.style.position = 'absolute';
nfTable.style.left = '-999em';

Now, we find each <tr> element and loop through, pushing each <th> into the label array and each <td> into the value array. Finally, we hide the table since we’ll replace it with the pie chart later.


  raphael("myHolderPie", 540, 400).pieChart(270, 200, 100, values, labels, "#fff");

To render the pie chart, we use the Raphaël object, specify an element id and some dimensions. In this case, we’re using the myHolderPie div. We give it a width of 540 pixels and a height of 400 pixels. We then attach the pieChart function to that area. The pieChart function takes several variables, including the center point from the left and right. In this case we are starting 270px from the left side of the holder element. The next argument is 200, which is the number of pixels from the top of the holder element. The 100 tells the code to make a pie chart with a diameter of 100 pixels. (Go ahead—try to increase that number to 200 or decrease it to 50—see what happens.) The next two arguments are the values for the pie chart and the labels we built using jQuery. The last argument is the HEX value for the wedge outlines. If you delete this argument, then there won’t be any borders.

gRaphaël has more information about the types of graphs that you can use to create graphics.

Keep in mind that every time you substitute a table or other information with a fancy graphic or interactive interface, you need to consider what you gain and what you lose. Accessibility is an important consideration. If your readers only have a keyboard, can they still use the tab key to access the data? Can blind users access the information in the new content as they could with the original? In this case an accessible table is replaced by a less-accessible asset, but the old table has been hidden and is still available to screen reader users. However, the new content does limit use by keyboard users. Support for the W3C’s Accessible Rich Internet Applications (ARIA) roles and for tabIndex=“0” in the SVG would help address accessibility constraints in the future, but these are not part of the Raphaël plugin today.

Animations

Raphaël also has plenty of interesting animation abilities built into it. One really interesting one is the ability to trace a path. Imagine you work for the Edinburgh Tourist council. The Edinburgh Castle is a popular destination, and you want to show the quicket and easiest way to get there from the train station. How cool it would be if you could plot a walking route!

Map of Edinburgh

Building this with Raphaël is a snap. First, you need a base map for the background. Second, you need to create a path to follow and finally a few lines of JavaScript to pull it all together.

First we need to create a Raphaël area in which to put all of our SVG. In this example, we’ll use the myMapHolder div and create an area that’s 540 pixels wide by 403 pixels tall.


  var myMap = raphael("myMapHolder", 540, 403);

Next we’ll drop in a background image of a map. I used a screen shot from Open Street Map, but you could use any image. To make sure users without JavaScript can still see the map, we add an <img> element with a link to the map image inside the myMapHolder div. The first thing we do with the JavaScript is remove this baseline image so the more dynamic map can be loaded in its place.


  document.getElementById("myMapHolder").removeChild(document.getElementById("myMapHolder").childNodes[0]);
  var myImage = myMap.image("map.png", 0, 0, 540, 403);

I positioned it at zero pixels from the top and at zero pixels to the left in the holder, then specified the dimensions of the image itself at 540 by 403 pixels.


  var myPath = myMap.path("M513.859,35.222l-95.332,24.667l" +
    "18.666,76.667l-131.334,46c0,0-10.667," +
    "3.333-11.333,13.333c-0.891,13.371-42.748," +
    "23.018-42.748,23.018s-13.252,3.648-9.252," +
    "18.315s14.667,43.333,14.667,43.333l-104.667,22.667")
    .attr({
    stroke: "#f00",
    opacity: .7,
    "stroke-width": 5
    });

Next comes the slightly tricky part: The code for the SVG path. I traced the map in Illustrator and saved the path as an SVG and pasted it in here, which was the easiest way to create a complex path quickly. As you can also see, we have attached the attr() function to specify the color, opacity, and width of the path. This will create a 70% opaque, red line that’s five pixels wide.


  var myMarker = myMap.ellipse(513.859,35.333, 7, 7)
    .attr({stroke: "none", 
    opacity: .7, 
    fill: "#f00"
    });

Once we’ve set up the map and the path, we need to create a small dot to “run the maze.” This is the code that creates a circle with a seven-pixel radius starting at point 513.859,35.333. We have also attached some additional attributes to remove the border, change the opacity, and the color of the dot.


  function run() {
    myMarker.animateAlong(p, 20000, false, function () {
      myMarker.attr({cx: 513.859 , cy: 35.333, rotation: 0});
      run();
    });
  }
  run();

Finally, we put it all together into an animation. We create a function called run(), which moves the dot along the path. When it’s finished executing, it resets and loops over and over again.

Now, to animate the dot, we place it into the variable myMarker and write myMarker.animationAlong(). This function takes several arguments: First we specify the path we want it to follow, in our case “myPath,” then we specify a duration for the length of time we want the dot to take as it travels the path. The next argument rotates the object along a path. We don’t want this effect, so we set this argument to false. Finally, we have a callback function, which in this case resets the dot to the starting position to create the loop.

You can see the full script and example put together so you can view source to adapt it as needed.

Benefits of SVG on the Web

As HTML5 grows in popularity, SVG is being rivaled by the <canvas> element. Both can draw graphics on the screen, which makes it confusing to know which to use. The biggest difference is that all the SVG elements are part of the DOM tree. With canvas, you can only access the root <canvas> element.

Since SVG is part of the DOM, you can add events to all the individual portions of an SVG graphic. Actions such as onClick, onTap or onHover all make SVG graphics intractable to the point of being a full Flash replacement. Since these events can be built up at run time, depending on the browser / device and its capabilities, it allows for a progressively enhanced experience. With <canvas> the drawing is not part of the DOM, and you sacrifice interactivity for a less memory intensive DOM tree. Injecting a complex SVG graphic could grind slower machines to a halt as they render thousands of DOM nodes.

SVG is scalable, so as more and more browsers move to the “zoom” rather than “scale” mechanism to increase font sizes and graphics, SVG keeps its smooth edges. The <canvas> element is rasterized which becomes pixelated.

SVG: a W3C standard

SVG is a W3C standard, which means that no one is going to take their ball and go home, leaving you stuck with loads of useless code. Since it’s not only open, but royalty-free, more and more applications, devices, and services are both consuming and vending SVG. A viable ecosystem has emerged to the point that SVG isn’t going to disappear. It also means that you don’t need to worry about the implementation details. The applications do all the hard work, creating the SVG nodes and paths for you. The raw XML is still there if you want to poke your head into the code and change it as needed.

Conclusion

SVG has been a maturing technology for several years. Now, with the support of all the major browser manufacturers (including IE9), it’s at a tipping point. The fact that we’re seeing dedicated JavaScript libraries such as Raphaël written by smart people like Dmitry Baranovskiy,  is just a sign of what’s to come. Making progressively enhanced, open-standard, vector-based graphics and animations with smooth, scalable curves, and odd-shaped clickable regions makes the future of the web pretty exciting!

29 Reader Comments

Load Comments