Animating Charts using D3
By: Anish Antony
Tags: Coding, Programming, JavaScript, D3js, Web Development

The SVG implementations are perfect if there is an animation on it. On this article lets discuss how to make animation on your SVG images using D3. SVG has an inbuilt capability for making animations/transformations. We can add that capability through d3.

Photo by Hans Eiskonen on Unsplash

One of the great use cases of SVG is you can make animations. Using the SVG animation's advantage, you can create simple games, animating characters, Graph and chart loading animations.

Here over this article, we are making a multiline chart with on load animation. If you want to know SVG animation's in-depth concept, please refer to my article about SVG animations.

Let's get start the coding….

These are the set of steps that we are discussing as part of this article.

First, we will set up the workspace and read the input data using the d3.js library. Next, we will make the line chart. Finally, we will create the chart on load animation.

Let's move on to the first step…. Setting up of workspace.

Step 1: Workspace setup

First, let's make to be ready our HTML page. For playing with d3 in your HTML page first, you need to add the d3 specific libraries on the HTML page.

Yeah ….. our workspace is ready to play with d3.js…

Let's start the coding for creating the SVG canvas with margin. The following code snippet makes an SVG canvas with a width of 630px and height of 400px .

var margin = {top: 20, right: 50, bottom: 30, left: 50},

width = 630 - margin.left - margin.right,

height = 400 - margin.top - margin.bottom;



var svg = d3.select("body").append("svg")

.attr("width", width + margin.left + margin.right)

.attr("height", height + margin.top + margin.bottom)

.append("g")

.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

Now our SVG canvas is ready. Here follow the SVG tags created based on the above code snippet.

<svg width="630" height="400">

<g transform="translate(50,20)"></g>

</svg>

Next, we need to get the data for two lines in the line chart. So we need to parse two data files and plot the result. Here I am taking the input as amazon and apple companies' last 6-month stock data. The data format looks like follows.

Here in this situation, we need to read data from two files. The implementation d3.queue helps to read from multiple resources at a time.

var parseDate = d3.timeParse("%m/%d/%Y"); d3.queue()

.defer(d3.csv, "/data/apple-6-months-data.csv")

.defer(d3.csv, "/data/amazon-6-months-data.csv")

.await(ready); function ready(error, apple, amazon) {

if (error) throw error;



apple.forEach(function(d) {

d.date = parseDate(d.date);

});

apple.forEach(function(d) {

d.close = Number(d.close.replace(/[^0-9.-]+/g,""));

}); apple.sort(function(a, b) {

return a.date - b.date;

}); amazon.forEach(function(d) {

d.date = parseDate(d.date);

});

amazon.forEach(function(d) {

d.close = Number(d.close.replace(/[^0-9.-]+/g,""));

}); amazon.sort(function(a, b) {

return a.date - b.date;

});

}

The above code snippet is an example of reading from multiple resources using d3.queue implementation. Once the resource reading finishes it will call the callback function called ready() with parameter as error, first file data, second file data . Inside of the ready function you can play with the file data.

In the above code snippet doing the following set of actions.

Read the file content using d3.queue . Once the file reading finished, it will trigger the ready function with error and file data. If there is an error, then the function throws the error . Parsing the apple date variable ( d.date ) type into the date from a string. Converting the apple closing balance into number type. Sorting the apple data based on date. Repeating steps 4,5,6 for amazon too.

Step 2: Creating a line chart

So we parse the data. Next, we are going to make our line chart using our parsed data. Here below follows the line chart plotting portion in our ready function

var xAxis = d3.axisBottom()

.scale(x)

.ticks(5)

.tickSizeInner(15)

.tickSizeOuter(0); var yAxis = d3.axisLeft()

.scale(y)

.tickFormat(d3.format("$.2f"))

.ticks(5)

.tickSizeInner(15)

.tickSizeOuter(0);

function ready(error, apple, amazon) {

.....

//apple - contains apple 6 months data

//amazon - contains amazon 6 months data

x.domain(d3.extent(apple, function(d) { return d.date; })); var amzn_ext = d3.extent(amazon, function(d) { return d.close; });

var apple_ext = d3.extent(apple, function(d) { return d.close; }); y.domain([

d3.min([amzn_ext[0],apple_ext[0]]),

d3.max([amzn_ext[1],apple_ext[1]])

]); svg.append("g")

.attr("class", "x axis")

.attr("transform", "translate(0," + height + ")")

.call(xAxis); svg.append("g")

.attr("class", "y axis")

.call(yAxis); svg.append("line")

.attr(

{

"class":"horizontalGrid",

"x1" : 0,

"x2" : width,

"y1" : y(0),

"y2" : y(0),

"fill" : "none",

"shape-rendering" : "crispEdges",

"stroke" : "black",

"stroke-width" : "1px",

"stroke-dasharray": ("3, 3")

});

var combinedData = [];

combinedData[0] = {name: 'apple', data: apple};

combinedData[1] = {name: 'amazon', data: amazon}; svg.selectAll(".plot-axis")

.data(combinedData)

.enter().append("g")

.attr("class", "plot-axis")

.append("path")

.attr("class", "line")

.attr("d", function(d) { return line(d.data); });

}

In the above code snippet, the variable apple contains apple 6 months data and the variable amazon contains amazon 6 months data. This data population explained in step 1. Here in the above code snippet, it is doing the following set of actions.

Using d3.extent module, it is finding out the maximum and minimum values of the last 6 months date range. For both Amazon and Apple the date range are the same here. So considering only the amazon. Finding out max and min values of amazon and apple closing balance using d3.extent. find out min and max of data from step 2. This is for deciding the y-axis min and max values. Plotting the x and y-axis using SVG. Creating a variable array combinedData which contains two values, which are apple and amazon 6 months data. Creating the line chart based on the combinedData array.

We are done with making the line chart. Next, we are going to the core idea behind this article, animating the line chart.

Step 3: Creating the animation

Now we are moving into the main part, adding animation. The below set of lines creates the loading animation for the SVG chart.

function ready(error, apple, amazon) {

...

svg.selectAll(".plot-axis")

.data(combinedData)

.enter().append("g")

.attr("class", "plot-axis"); var path = svg.selectAll(".plot-axis").append("path")

.attr("class", "line")

.attr("d", function(d) { return line(d.data); });



d3.select(path._groups[0][0])

.attr("stroke-dasharray", totalLength[0] + " " + totalLength[0] )

.attr("stroke-dashoffset", totalLength[0])

.transition()

.duration(5000)

.ease(d3.easeLinear)

.attr("stroke-dashoffset", 0); d3.select(path._groups[0][1])

.attr("stroke-dasharray", totalLength[1] + " " + totalLength[1] )

.attr("stroke-dashoffset", totalLength[1])

.transition()

.duration(5000)

.ease(d3.easeLinear)

.attr("stroke-dashoffset", 0);



}

The above line of code doing the following set of actions.

Create two paths based on the combinedData array. Iterating path._groups[0][0] and apply linear transition for the duration of 5 sec. Iterating path._groups[0][1] and apply linear transition for the duration of 5 sec.

Here follows the complete set of code for line chart implementation.

The output of the above code snippet looks like follows.

In this article, we saw how to make basic animations on the line chart using d3. In D3 official site, you can see more advanced examples of d3 charts.