SVG with a little help from Raphaël
Issue № 310

SVG with a little help from Raphaël



Article Continues Below

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#section2

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#section3

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#section4

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#section5

Progressive Enhancement#section6

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
Calories 45%
Saturated Fats 25%
Protein 15%
Vitamin B 10%
Vitamin C 5%

function pieMe() { 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'; Raphael("myHolderPie", 540, 400).pieChart(270, 200, 100, values, labels, "#fff"); } pieMe();

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#section7

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

function tracePath(){ document.getElementById("myMapHolder").removeChild(document.getElementById("myMapHolder").childNodes[0]); var r = Raphael("myMapHolder", 540, 403), c = r.image("map.png", 0, 0, 540, 403), p = r.path("M513.859,35.222l-95.332,24.667l18.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}),e = r.ellipse(513.859,35.333, 7, 7).attr({stroke: "none", opacity: .7, fill: "#f00"});

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

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#section8

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#section9

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#section10

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

  1. I was working with SVG awhile back, and found it a little cumbersome. Maybe I wasn’t doing it quite right. My ideal situation would be to create a graphic in Illustrator, save as SVG, and upload to the web like I do with other image formats. I’d also like to be able to specify some parts of it (like colors) through PHP before the image is delivered.

    I found that to use SVG images in a web page, I needed to run a “PHP script”:http://bkp.ee/atirip/ that would first convert the image to a format Raphael could read, and then Raphael could render the image. It seemed to slow things down quite a bit. And I never was able to get a PHP file to load as an SVG even when I changed the content type in the header to image/svg+xml.

    I’m really excited about the possibilities of SVG, but I wonder if I’m missing something about its implementation. I’ll definitely try the progressively enhanced charts described here.

  2. That was awesome. I’ve never really had the time to dive into SVG, but I’m extremely excited about it now. Could you give a little more detail about how to create a path in Illustrator and export it to SVG? I’m sure it’s figure-out-able or Google-able, but I thought it could stand to be part of the example. Great piece.

  3. That is some seriously awesome technology. I don’t know why I never even thought of taking a table and making it in to a chart using Javascript and SVG. It just seems like one of those things you’d just do in Excel and save it as an image. I’m really enjoying this transition of using images only when they’re needed. Photos of people and places for example. Graphics that are relevant to your content can be better represented by legible data than binary images. Not to mention it will download much quicker. I think I’ll start playing with Inkscape some more 🙂

  4. Sean McCambridge, depending on your version, it might vary. Basically using the Pen tool, you draw or trace the route you want. Be sure your image is at zero,zero when you create a path, otherwise it will be offset when you put it into Raphael.

    You can Export or Save As… SVG. Somewhere in the code there will be an element called . The text inside the d attribute describes you path.

    Your mileage may vary, but that is the quickest and easiest way to get the encoded path. You could read-up on how it all works and do it by hand. Once you get the hang of it, for simple paths it isn’t hard. Understanding SVG paths could be an article in itself!

  5. I _love_ the look of your bouncing piechart. I’m having difficulties finding out how you styled it to look and behave. Can you share how to style my default pie chart to look like yours?

    thanks!

  6. Ive seen Raphael around but haven’t had a project to use it on yet. This article did a great job showing a useful application of progressive enhancement. I’m stoked.

    ps: the animation function uses the var p for the path but the example code to create a path uses myPath. Just a heads up in case this could confuse someone.

  7. This is a very good example of interactive web using Raphael. Raphael has many facets to it. One of the interesting uses that I have put it to recently is to create a general purpose online editor to create, edit and share vector images.

  8. I had this page open in Firefox 3.6.6, and my CPU was 50%. 100% of my Intel E8400 dual core. I think there are some performance issues to iron out.

    I do like SVG though. For dynamically drawing charts etc, but even more for having a static scalable (vector) image format on the web!

  9. ” including the center point from the left and right. ”
    ought to read ” from the left and top.”

  10. In Opera 10.60 and Firefox 3.6.6 CPU load grows up to 100% (a bit worse in Opera) causing page to sroll twitchy on my 1,8 GHz Athlon 64 with 768 MB of RAM. And it gets even worse when trying to manipulate page tabs.

    I like the concept of SVG (scripting, scaling, etc.), but I don’t think I would use it in a next few years. And that’s sad.

    Disregard that, article is great!

  11. I already knew about this library and I think it works great on a PC, however I’ve couldn’t watch the page demos on my cell phone, as the article stated (Using webkit browser on android) Did this work for anyone?

  12. nice article!

    but in the section with the animation along the path, shouldn’t it be

    @function run() {
    myMarker.animateAlong( myPath@ “¦

    instead of

    @function run() {
    myMarker.animateAlong( p@ “¦

  13. First off – yes, patrick, I thought that too, you’re right.

    Mainly, though, what a great article, thanks. I’m working on a project for a client which is starting off in InDesign and Illustrator and going into an HTML format. I was going to convert all the .ai’s into jpegs, but now I’m seriously considering using svg.

    Thanks!

  14. It’s nice to create graphs and data visualizations from hard-HTML-data, but all this extra programming gives me a headache! wish there was a UI to create all this… Or, that Apple supported Flash. Flash can utilize XML data which can just as easily be turned into search engine-friendly HTML, too.

  15. TeviH, there is substantial HTML that could be replaced with a simple image or flash object, but then you lose all the benefits. With progressive enhancement, you start with the basic HTML which is accessible to everyone, including search crawler bots indexing your content. Then you layer on more and more beautiful designs and functionality as the browser accepts it. Had this been done with a image or plugin, it would never appear in search results or via screen readers or devices that don’t support plugins.

    It is a trade-off of how much code you want to write versus the reward. In this case, I’d say the reward is pretty high for the extra effort of progressive enhancement.

  16. I really love Raphaël, but I have to concur that it is quite prone to performance issues.

    Is anyone aware of any optimization techniques, or is this merely a limitation of SVG?

  17. First of all great article. I like it and I will try it. But I can´t see why it is an “full flash replacement”. I see the benefits of not relaying on a plugin. But the animations are stuttering and not smooth, as pure JS animations also are. I am not a flash fan boy, but in my opinion flash animations are still nicer and smoother.

  18. I do have a great concern that with libraries like this the line between code and design is blurring again.

    I tried to get my head around Raphael after the recent .net piece on it and struggled to make anything happen. I found the learning curve was quite high. I would imagine that for Flash developers wanting to abstract their work into ‘html5’ based data visualisations instead of flash charts this will come with a reasonable amount of ease.

    For those of us not so accustomed with drawing objects with code instead of a paintbrush or a pencil I think there is a real high learning curve.

  19. Is this a programming issue?

    I’ve looked at the main article page in two versions of FF (3.0.19 & 2.0.0.20) and in both cases the firefox process jumps to take something between 89 & 99% CPU, varying continually. This processor use appears to continue indefinately although the page appears to be fully rendered, only stopping when I close the tab or navigate to another page.
    The newer FF version at least says the page is fully loaded, whilst the older one says it is still loading – in between times 20 minutes after the page was fully readable.

    My guess is that we have an endless loop somewhere.

    Otherwise a great article and a very intresting step forward.

    Alan

  20. I can not identify/isolate the problem but, firefox (3.6.7) uses 0-@ cpu time on article page. But insonsistantly (most dangerous form of leak I think, makes very hard to identify problem.),

    Imho svg is not quite ready for animation. Yes it’s good for fun, but looks dangerous for real life implementation. Specially for intensive markets charts etc.

    secondly when it comes to fun; I rather choose processing js.
    http://processingjs.org/

  21. The performance of this particular page is bad, not because SVG is “not quite ready for animation” or Raphaël has a memory leak, but because page has infinite loop. Little red dot goes along the curve all the time: calculating dot position is quite expensive operation and doing this all the time takes a lot of CPU attention. So, it is not any technology fault, but rather unfortunate example. And it is my fault that I didn’t notice it and didn’t warn Brian.

  22. Many thanks Dmitry, for your explanation and the insight it provides. Maybe it’s not so bad an example after all, demonstrating the limits of the method. Most authors of such articles don’t do this. 🙂

  23. I’m with Blaise. Firefox pegged one of my 2.4ghz cores (and the laptop fan kicked on) while viewing just those two images, not actually doing anything with them. I opened the page in IE and that pegged the other core. Clearly this approach isn’t ready for primetime yet.

  24. I do hope we can get keyboard access to provide all the same functions currently performed in mouse functions. We review Adobe Flash applications and fail them for Section 508 compliance when they don’t have keyboard access. SVG needs to have keyboard access, a way to focus on individual elements, and while I’m dreaming a perfect dream, color and contrast that respect the user’s system settings.

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

    Where is this ?

  26. How do you compare Raphael to thejit & processing.js regarding interactivity and dynamic manipulation (realtime data) of graphs/charts in runtime.

  27. I totally agree with what you said on how it performs with this kind of specs because I have the same experience too! But I still do hope that they would continue to use it over a period of time possible.

    margo from Escalier escamotable 

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA