D3.js – Working Example


D3.js – Working Example


”;


Let us perform an animated bar chart in this chapter. For this example, we take the data.csv file used in the previous chapter of the population records as dataset and generate an animated bar chart.

To do this, we need to perform the following steps −

Step 1Apply styles − Apply CSS styles using the coding given below.

<style>
   .bar {
      fill: green;
   }
   
   .highlight {
      fill: red;
   }
   
   .title {
      fill: blue;
      font-weight: bold;
   }
</style>

Step 2Define variables − Let us define the SVG attributes using the script below.

<script>
   var svg = d3.select("svg"), margin = 200,
   width = svg.attr("width") - margin,
   height = svg.attr("height") - margin;
</script>

Step 3Append text − Now, append text and apply the transformation using the coding below.

svg.append("text")
   .attr("transform", "translate(100,0)")
   .attr("x", 50)
   .attr("y", 50)
   .attr("font-size", "20px")
   .attr("class", "title")
   .text("Population bar chart")

Step 4Create scale range − In this step, we can create a scale range and append the group elements. It is defined below.

var x = d3.scaleBand().range([0, width]).padding(0.4),
   y = d3.scaleLinear()
      .range([height, 0]);
   var g = svg.append("g")
      .attr("transform", "translate(" + 100 + "," + 100 + ")");

Step 5Read data − We have already created the data.csv file in our previous examples. The same file, we have used here.

year,population
2006,40
2008,45
2010,48
2012,51
2014,53
2016,57
2017,62

Now, read the above file using the code below.

d3.csv("data.csv", function(error, data) {
   if (error) {
      throw error;
   }

Step 6Set domain − Now, set the domain using the coding below.

x.domain(data.map(function(d) { return d.year; }));
y.domain([0, d3.max(data, function(d) { return d.population; })]);

Step 7Add X-axis − Now, you can add the X-axis to the transformation. It is shown below.

g.append("g")
   .attr("transform", "translate(0," + height + ")")
   .call(d3.axisBottom(x)).append("text")
   .attr("y", height - 250).attr("x", width - 100)
   .attr("text-anchor", "end").attr("font-size", "18px")
   .attr("stroke", "blue").text("year");

Step 8Add Y-axis − Add the Y-axis to the transformation using the code given below.

g.append("g")
   .append("text").attr("transform", "rotate(-90)")
   .attr("y", 6).attr("dy", "-5.1em")
   .attr("text-anchor", "end").attr("font-size", "18px")
   .attr("stroke", "blue").text("population");

Step 9Append group elements − Now, append the group elements and apply transformation to Y-axis as defined below.

g.append("g")
   .attr("transform", "translate(0, 0)")
   .call(d3.axisLeft(y))

Step 10Select the bar class − Now, select all the elements in the bar class as defined below.

g.selectAll(".bar")
   .data(data).enter()
   .append("rect")
   .attr("class", "bar")
   .on("mouseover", onMouseOver) 
   .on("mouseout", onMouseOut)
   .attr("x", function(d) { return x(d.year); })
   .attr("y", function(d) { return y(d.population); })
   .attr("width", x.bandwidth())
   .transition()
   .ease(d3.easeLinear)
   .duration(200)
   .delay(function (d, i) {
      return i * 25;
   })
   .attr("height", function(d) { return height - y(d.population); });
});

Here, we added the listener event for the mouseout and mouseover to perform animation. It applies the animation, when the mouse hovers over a particular bar and goes out of it. These functions are explained in the following step.

The .ease(d3.easeLinear) function is used to perform apparent motion in animation. It processes the slow-in and the slow-out motion with a duration of 200. The delay can be calculated using −

.delay(function (d, i) {
   return i * 25;
})

Step 11Mouseover event handler function − Let us create a mouseover event handler to handle a mouse event as shown below.

function onMouseOver(d, i) {
   d3.select(this)
      .attr(''class'', ''highlight'');
   d3.select(this)
      .transition()
      .duration(200)
      .attr(''width'', x.bandwidth() + 5)
      .attr("y", function(d) { return y(d.population) - 10; })
      .attr("height", function(d) { return height - y(d.population) + 10; });
   g.append("text")
      .attr(''class'', ''val'') 
   
   .attr(''x'', function() {
      return x(d.year);
   })
   
   .attr(''y'', function() {
      return y(d.value) - 10;
   })
}

Here, in the mouseover event, we want to increase the bar width and height, and the bar color of the selected bar to red. For the color, we have added a class ‘highlight’, which changes the color of the selected bar to red.

A transition function to the bar for the duration of 200 milliseconds. When we increase the width of the bar by 5px, and the height by 10px, the transition from the previous width and height of the bar to the new width and height will be for the duration of 200 milliseconds.

Next, we calculated a new ‘y’ value to the bar, so that the bar does not distort due to the new height value.

Step 12Mouseout event handler function − Let us create a mouseout event handler to handle a mouse event. It is defined below.

function onMouseOut(d, i) {
   d3.select(this).attr(''class'', ''bar'');
   
   d3.select(this)
      .transition()     
      .duration(400).attr(''width'', x.bandwidth())
      .attr("y", function(d) { return y(d.population); })
      .attr("height", function(d) { return height - y(d.population); });
   
   d3.selectAll(''.val'')
      .remove()
}

Here, in the mouseout event, we want to remove the selection features that we had applied in the mouseover event. Therefore, we revert the bar class to the original ‘bar’ class and restore the original width and height of the selected bar and restore the y value to the original value.

The d3.selectAll(‘.val’).remove() function is used to remove the text value we had added during the bar selection.

Step 13Working Example − The complete program is given in the following code block. Create a webpage animated_bar.html and add the following changes to it.

<!DOCTYPE html>
<html>
   <head>
      <style>
         .bar {
            fill: green;
         }
        
         .highlight {
            fill: red;
         }
         
         .title {
            fill: blue;
            font-weight: bold;
         }
      </style>
      <script src = "https://d3js.org/d3.v4.min.js"></script>
      <title> Animated bar chart </title>
   </head>

   <body>
      <svg width = "500" height = "500"></svg>
      <script>
         var svg = d3.select("svg"),
         margin = 200, width = svg.attr("width") - margin,
         height = svg.attr("height") - margin;
         
         svg.append("text")
            .attr("transform", "translate(100,0)")
            .attr("x", 50).attr("y", 50)
            .attr("font-size", "20px")
            .attr("class", "title")
            .text("Population bar chart")
            
         var x = d3.scaleBand().range([0, width]).padding(0.4),
         y = d3.scaleLinear().range([height, 0]);
            
         var g = svg.append("g")
            .attr("transform", "translate(" + 100 + "," + 100 + ")");

         d3.csv("data.csv", function(error, data) {
            if (error) {
               throw error;
            }
               
            x.domain(data.map(function(d) { return d.year; }));
            y.domain([0, d3.max(data, function(d) { return d.population; })]);
                     
            g.append("g")
               .attr("transform", "translate(0," + height + ")")
               .call(d3.axisBottom(x))
               .append("text")
               .attr("y", height - 250)
               .attr("x", width - 100)
               .attr("text-anchor", "end")
               .attr("font-size", "18px")
               .attr("stroke", "blue").text("year");
               
            g.append("g")
               .append("text")
               .attr("transform", "rotate(-90)")
               .attr("y", 6)
               .attr("dy", "-5.1em")
               .attr("text-anchor", "end")
               .attr("font-size", "18px")
               .attr("stroke", "blue")
               .text("population");
                         
            g.append("g")
               .attr("transform", "translate(0, 0)")
               .call(d3.axisLeft(y))

            g.selectAll(".bar")
               .data(data)
               .enter()
               .append("rect")
               .attr("class", "bar")
               .on("mouseover", onMouseOver) 
               .on("mouseout", onMouseOut)   
               .attr("x", function(d) { return x(d.year); })
               .attr("y", function(d) { return y(d.population); })
               .attr("width", x.bandwidth()).transition()
               .ease(d3.easeLinear).duration(200)
               .delay(function (d, i) {
                  return i * 25;
               })
                  
            .attr("height", function(d) { return height - y(d.population); });
         });
          
          
         function onMouseOver(d, i) {
            d3.select(this)
            .attr(''class'', ''highlight'');
               
            d3.select(this)
               .transition()     
               .duration(200)
               .attr(''width'', x.bandwidth() + 5)
               .attr("y", function(d) { return y(d.population) - 10; })
               .attr("height", function(d) { return height - y(d.population) + 10; });
              
            g.append("text")
               .attr(''class'', ''val'')
               .attr(''x'', function() {
                  return x(d.year);
               })
               
            .attr(''y'', function() {
               return y(d.value) - 10;
            })
         }
          
         function onMouseOut(d, i) {
             
            d3.select(this)
               .attr(''class'', ''bar'');
            
            d3.select(this)
               .transition()     
               .duration(200)
               .attr(''width'', x.bandwidth())
               .attr("y", function(d) { return y(d.population); })
               .attr("height", function(d) { return height - y(d.population); });
            
            d3.selectAll(''.val'')
               .remove()
         }
      </script>
   </body>
</html>

Now, request the browser and we will see the following response.