Previous Material

For these notes to be useful, make sure that you have already covered the material in:

Transitions

If you followed along with the notes of the Hello World tutorial in D3.js, at the end of tutorial you had a simple animation where first the text "Hello World!" appears, then the two circles move from their corners toward the middle, and finally another text phrase ("Uh, hi") appears. This animation was made possible by the three operations: .transition(), .delay(), and .duration(), which can be combined together to create different effects.

.transition() - this is the first operator that we need to start an animation. It can be followed by any of the other modification operators that we've seen, such as .style() or .attr(). The purpose of transition is to change the properties/attributes specified in .style() or .attr() from their momentarily values to the new ones specified as an argument to the operators. By default, a transition lasts for 250ms, which is pretty fast. This is where .duration() comes into play.

.duration() - this operator takes as an argument the amount of milliseconds we want a transition to last. For example, if the purpose of the transition is to change the position of an element along the vertical axis, then .duration() specifies how long it takes for the element to move from its current position to the new one.

          d3.select("circle")
            .transition()
            .duration(2000)
            .attr("cy", 400)
       

.delay() - this operator also takes as its first argument an amount of time in milliseconds. Its purpose is to wait for exactly that amount of time, before allowing the transition to happen. This is useful if you want to "orchestrate" an animation by timing the entrance or departure of the elements according to your vision.

As we discussed in our Introduction to D3.js notes most D3 operators can take as argument a function, known as a functional operator, which, by default has two parameters: the bound data item and the index of the element in the selection. Using such a functional operator with .delay() has the powerful effect of programatically delaying the transition of all elements in a selection. See the example below for a demonstration.


Below is the code the implements the animation above. Remember, we need an SVG element if we want to draw shapes such as circles, lines, polines, paths, etc.

<svg id="svg1"width=260 height=260 
      style="border: 1px solid lightgray">&ls;/svg> 
      
<script>
var points = [{'x': -10, 'y': 5}, {'x': -10, 'y': 55},
              {'x': -10, 'y': 105}, {'x': -10, 'y': 155},
              {'x': -10, 'y': 205}, {'x': -10, 'y': 255}];
              
d3.select("#svg1").selectAll("circle")
  .data(points)
  .enter()
  .append("circle")
  .attr("r", 5)
  .attr("cx", function(d){return d.x})
  .attr("cy", function(d){return d.y;})
  .style("fill", "red")
  .transition()
  .delay(function(d,i){return i*300})
  .duration(3000)
  .attr("cx", 265)
  .remove();
</script>
    

In order to understand this code better, open the Inspect Element, find in the DOM tree the SVG element with ID of "svg1" (it is nested within the DOM, collapse nodes to find it), and then click the Run button to see what is changing in the DOM tree.

Transitions for elements created with SVG

The code shown in the previous section generates the circle elements with D3. In the lecture notes about SVG, we showed how we can create elements by writing SVG code. Given that for AM5, some of you will create their SVGs in an external editor and then export it to HTML, I'm showing here an example of how to manipulate existing SVG elements to perform D3 animations.

We'll start with the SVG code that creates four circles with different colors, all positioned outside of the borders of the SVG element, so that there are not visible initially. We know that they are outside the borders, because the SVG is 300x300 and the coordinates for the origin of the circle are either negative (-20) or larger than the container (320).

<svg width="300" height="300" style="border: 1px solid gray">
    <circle id="c1" r=20 cx=-20 cy=-20 fill="pink"></circle>
    <circle id="c2" r=20 cx=320 cy=-20 fill="purple"></circle>
    <circle id="c3" r=20 cx=-20 cy=320 fill="lightgray"></circle>
    <circle id="c4" r=20 cx=320 cy=320 fill="skyblue"></circle>
</svg>
<p><button id="an2">Run</button>
       


This is the code for the animation above. When the button is clicked, the function animation is invoked.

function animation(){
    
    d3.select("#c1")
    .transition()
    .duration(2000)
    .attr({"cx": 320, "cy": 320});
    
    d3.select("#c2")
    .transition()
    .duration(2000)
    .attr({"cx": -20, "cy": 320});
    
    d3.select("#c3")
    .transition()
    .delay(1000)
    .duration(2000)
    .attr({"cx": 320, "cy": -20});
    
    d3.select("#c4")
    .transition()
    .delay(1000)
    .duration(2000)
    .attr({"cx": -20, "cy": -20});
}

d3.select("#an2").on("click", animation);
        

Run the animation. Then try to run it again. Is it working? Why not? Open the Inspect Element console, look at the DOM to find the code for this animation and find out what is preventing the animation from running for a second time.

Bar Charts with HTML elements

If you have already completed the Working with Data tutorials, you've seen there how to build a bar chart using SVG rectangles. That is though not the only way to built a bar chart, we can use plain HTML elements such as p or div to do the same. The example below shows how. It also shows data binding in action, given that we can enter the data that we want in the input box.

I am Harry Potter.

I am Hermione Granger.

I am Ron Weasley.

I am Drago Malfoy.

I am Luna Lovegood.

I am Cho Chang.

       
# event binding       
d3.select("#chartButton").on("click", create);
    
function create(){
     
  // get values from the user input
  var values = document.querySelector("#ui input").value.split(" ");

  // update: the intersection of existing DOM elements and data points
  var p = d3.select("#barchartDiv").selectAll("p")
          .data(values)
          .text(String)
          .style("width", function(d){ return d*10+"px";});

  p.enter().append("p")
          .text(String)
          .style("width", function(d){ return d*10+"px";});    

  p.exit().remove();
}  
 
# event binding
d3.select("#sortButton").on("click", mySort);
    
function mySort(){
  // this makes sense only if we have already bound data to the nodes
  d3.select("#barchartDiv").selectAll("p")
    .sort(function(a,b){return a - b;});
}

Notice the easy way to bind event handlers to event attributed in D3. We first select the element, then use the operator .on() with two arguments: the event attribute, and the function name for the event handler.

What is Next?

If you choose the artistic track for AM5, you will need to master the concept of transitions.

This tutorial on the transitions does a great job of explaining step-by-step how to build an animation that runs constantly. Cool fact: It uses recursion. You write a function which will be keeping invoking itself forever.

If you choose the programming track for AM5, you will need to become good at data binding and how to format the data before preparing the graphics. Therefore, completing and understanding the Working with Data tutorial is a must.

Once you've read through the class notes, and feel that you need to start all over again, because things haven't clicked yet, a slower introduction to D3 can be found in this tutorial, which is also a book. The author of the tutorial/book, Scott Murray, has a very interesting homepage displaying a D3 animation.

An even more gentle introduction to D3 is provided by the Dashing D3.js website. Try it out and see if you like it.

Once you have done that and you're feeling hungry for more D3.js, then you should visit the page of D3 tutorials hosted on Github