Creating Interactive Graphs With SVG, Part 1

- - posted in animation, graphs, svg, visualization | Comments

In a series of two posts we take a look at building interactive graphs with SVG, CSS and Javascript. We take a real-world example from one of our projects and show you our approach. The final goal is an interactive graph (think animations, shadows and tooltips) using a jQuery-SVG plugin.

This article originally appeared on the Inspire.nl Blog. Inspire is a Dutch software studio that develops innovative Ruby on Rails web applications for computer, tablet and mobile.

Introduction

For the Elsevier Journal Insights project we wanted to create interactive graphs in the browser. The type of graphs are different, but one of the simplest among them is a regular line-graph. We chose the jQuery-SVG library to let us interact with a graph built with SVG. An other popular library is Raphaël, but we decided to go for the former because it seemed to give us more freedom while keeping close the SVG specifications.

In this series of articles we will show and discuss how we built one of those line-graphs, using HTML/SVG, Javascript and CSS. In this first article the structure of the graph is analyzed and constructed. In the following post we will look at actually using the jQuery-SVG library to interact with the DOM and animate properties and elements of the graph.

So what is the structure of this line-graph? When we analyze the graph on the right, we can identify a few parts:

  • A visual grid consisting of white dashed lines. These help the user to interpret the value of the data points.
  • The data points, each belonging to a year and with a value, represented as white/colored disc.
  • Underneath the data points lies a surface, giving an indication of the volume underneath the data points.
  • Textual labels at the axis determining the domain and range of the graph.

In the following sections of this article we look at each of these parts and how to construct them using HTML, SVG and CSS.

The grid

For the lines of the visual grid we first need to determine the dimensions of the canvas we work on and the range of the data we need to display. Lets assume the final html element will have a width of 720 pixels and a height of 410 pixels. We want to plot the value of something over a period of time, the domain of the graph. In this case; we have five domain points (a year for which there is a value) and we have established that our range goes from 0-15. The grid system we want consists of vertical lines for every year and horizontal lines for every 2.5 points on the range. Keeping in mind we need some margins on sides, we can create the following grid:

Background grid lines HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<g class="grid x-grid" id="xGrid">
  <line x1="113" x2="113" y1="10" y2="380"></line>
  <line x1="259" x2="259" y1="10" y2="380"></line>
  <line x1="405" x2="405" y1="10" y2="380"></line>
  <line x1="551" x2="551" y1="10" y2="380"></line>
  <line x1="697" x2="697" y1="10" y2="380"></line>
</g>
<g class="grid y-grid" id="yGrid">
  <line x1="86" x2="697" y1="10" y2="10"></line>
  <line x1="86" x2="697" y1="68" y2="68"></line>
  <line x1="86" x2="697" y1="126" y2="126"></line>
  <line x1="86" x2="697" y1="185" y2="185"></line>
  <line x1="86" x2="697" y1="243" y2="243"></line>
  <line x1="86" x2="697" y1="301" y2="301"></line>
  <line x1="86" x2="697" y1="360" y2="360"></line>
</g>

You will see the use of the <g> element. This acts as a grouping element, or a container. It has no visual properties of its own.

Using CSS we can create a nice subtle dashed style for these lines:

Background grid lines CSS
1
2
3
4
5
6
7
8
9
10
svg.graph {
  height: 500px;
  width: 800px;
}

svg.graph .grid {
  stroke: white;
  stroke-dasharray: 1 2;
  stroke-width: 1;
}

With the grid in place, we should know were every data point belongs.

Data points

So; we now have our grid and we know how to calculate positions for domain and range points on that grid. The next step is to draw some real data points on that grid. For this we use the circle element, and again we wrap them all in a grouping element:

Data points HTML
1
2
3
4
5
6
7
<g class="first_set points" data-setname="Our first data set">
  <circle cx="113" cy="192" data-value="7.2" r="5"></circle>
  <circle cx="259" cy="171" data-value="8.1" r="5"></circle>
  <circle cx="405" cy="179" data-value="7.7" r="5"></circle>
  <circle cx="551" cy="200" data-value="6.8" r="5"></circle>
  <circle cx="697" cy="204" data-value="6.7" r="5"></circle>
</g>

Here we use some data- attributes to keep track of some information assigned to this data set. The data-setname can be used for a tooltip when inspecting this data point, and the value of data-value gives a precise numeric representation of the data point.

When we use both the stroke and fill styling properties of circles, we can create the effect of double circles and create the white/colored discs:

Data points CSS
1
2
3
4
5
6
7
svg.graph .points {
  stroke: white;
  stroke-width: 3;
}
svg.graph .first_set {
  fill: #00554d;
}

The data points now need some form of connection between them; a clear visual guide to relate them to the overall graph and their siblings.

Surface

To create a more clear visual indication of our data points, we like to color the area underneath the lines (an other option would be to connect the data points with a line). For this we use the <path> element of svg. Using this element, we can create a polygon of any shape which will appear as a surface in our graph. You can look at is like putting your pen down on a piece of paper and moving it, without lifting it from the paper, to the specified coordinates (this is closely related to my first programming education on primary school; LOGO and Turtle Graphics). More information on the <path> element can be found in the path specifications. The important commands are:

  • M(x,y): move the pen to the absolution point (x,y)
  • L(x,y): draw a straight line to the absolution point (x,y)
  • Z: close the current path and draw a straight line to the initial point

So to create our surface we have to start (M) in the lower left corner, draw a line (L) up to the first point and then follow all the points using the L command again. We end the path with a Z, after specifying the lower right corner of our graph. You can see that the coordinates of the path are the same as for the data points.

Surface HTML
1
2
3
<g class="surfaces">
  <path class="first_set" d="M113,360 L113,192 L259,171 L405,179 L551,200 L697,204 L697,360 Z"></path>
</g>

The color of this surface is already defined when we created that data points, but we would to keep the grid visible. One solution is to make the surface transparent:

Surface CSS
1
2
3
svg.graph .surfaces {
  fill-opacity: 0.5;
}

Although this works for a single surface, this does not work when we have multiple data sets and surfaces in our graph. That is because when we stack multiple transparent elements, the background comes less and less visible. So we need to redraw the grid over de surface again. The dirty way would be to copy the <g> elements of our surface, but SVG offers us a nice trick with the <use> element. With that element we can refer to an other element and let the SVG internally copy it. Notice that we have created id attributes for the g elements of the grid. Using a xlink:href path, we can display them above the surfaces:

Re-use of grid lines HTML
1
2
<use class="grid double" xlink:href="#xGrid" style=""></use>
<use class="grid double" xlink:href="#yGrid" style=""></use>

To keep the visual effect of transparency, we give these line a stroke-opacity, so it looks they are under the surface:

Re-use of grid lines CSS
1
2
3
svg.graph .grid.double {
  stroke-opacity: 0.4;
}

We now have all the visual elements of our graph; the grid for visual guidance, the data points and a surface under the data points. What we still need is some textual indication of what we are looking at.

Labels

SVG offers us the nice ability to add text elements which can act as labels, but the CSS-settings are a bit different than for normal html elements. Instead of the text-align property, we can use text-anchor which accepts values begin, middle and end. For the labels with the vertical lines, we use the middle property so that every year is centered on the line. For the labels which indicate a value for the horizontal lines, we use end so the right side of the label is on the same place.

Text labels HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<g class="labels x-labels">
  <text x="113" y="400">2008</text>
  <text x="259" y="400">2009</text>
  <text x="405" y="400">2010</text>
  <text x="551" y="400">2011</text>
  <text x="697" y="400">2012</text>
</g>
<g class="labels y-labels">
  <text x="80" y="15">15</text>
  <text x="80" y="131">10</text>
  <text x="80" y="248">5</text>
  <text x="80" y="365">0</text>
  <text x="50" y="15">Weeks</text>
</g>

Other regular css-font properties can also be applied to the svg text elements. We use the kerning property to separate each character a bit from the other.

Text labels CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
svg.graph .labels {
  font-family: Arial;
  font-size: 14px;
  kerning: 1;
}

svg.graph .labels.x-labels {
  text-anchor: middle;
}

svg.graph .labels.y-labels {
  text-anchor: end;
}

Wrap-up

We just have created all the elements we need to create a graph. When we combine it all in a single <svg> element, we get the result as in this jsFiddle or this stand alone page. Notice that the order of the surfaces and points are switched; otherwise the surface falls over the points.

In our original graphs we use multiple data sets. In this system that is very easy; for each data set you can create a g.points group elements with the points and a path element for the surface. With the surfaces you need to be careful; we know that for our data the values of one set are always equal or larger than to the other. If that is unknown, the data points can “disappear” behind other surfaces.

In the next post we will show how you can interact with this graph using jQuery. Then you are able to add some nice animations (e.g. when you hover over a data point) and add a tooltip with information (remember the data-value attribute we used?).

This blog post appeared earlier on the Inspire.nl blog, where I was lead developer on the Elsevier Journal Insights project.

Comments