For these notes to be useful, make sure that you have already covered the material in:
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.
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.
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.
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