This animation uses d3.js and a small amount of data. The idea is to mirror the lines of the nursery rhyme.
First a set of data is defined in Javascript arrays:
var colours = ["Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet"]; var order = [0, 2, 6, 3, 5, 1, 4]; var delay = [100, 1000, 1000, 1000, 750, 1000, 1000]
These control the colours of the parts of the rainbow (colours
), the
order in which they will appear (order
), and the delays between each
one appearing (delay
).
Next, we define some configuration variables to control how large the rainbow is, and how much space is left around it:
var margin = { top: 30, right: 200, bottom: 50, left: 30, pad: 10 }; var width = 800 - margin.left - margin.right; var height = 350 - margin.top - margin.bottom;
In order to draw the rainbow, we need to use SVG or Structured Vector Graphics. That means we need to add some elements to the HTML page. We do that with the following code:
var svgContainer = d3 .select(".rainbow") .append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(300,300)");It asks D3 to select the
<div>
element which has the CSS class
rainbow
, and add a new <svg>
element to it.
Next, it sets the width and height so that our graphic is big enough. Inside that
it add a new <g>
element, which denotes graphics. It then
moves in 300 pixels from the top and from the left before starting to draw.
(Translate is the technical term for move.)
After that, we have to define an arc, which is the maths name for part of a circle. Arcs are defined by a starting angle, an ending angle and a radius. It easy to think of drawing a circle, but only putting the pen on the paper between the start and end angles.
var arc = d3 .arc() .innerRadius(function (d) { return 300 - 20 * d; }) .outerRadius(function (d) { return 300 - 20 * d - 20; }) .startAngle(function (d) { return (-Math.PI / 2) * 0.7; }) .endAngle(function (d) { return (Math.PI / 2) * 0.7; });Computers do trigonometry slightly different to us, they don't use degrees for angles as the distance between each degree changes depending on how big the circle is. Instead they use something called radians. One radian is the same no matter how big the circle is, and is roughly equal to 57 degrees. A full circle goes from 0 to 2 x pi. We want to have a length the 70% of our circumference of our circle (so the length is 0.7 x pi) but centred on the line that points straight up and down. That's why the angles go from
Math.PI / 2
to - Math.PI / 2
.
The innerRadius
is the length of the bottom (or in our case smaller) side
of the rainbow lines, and the outerRadius
is the larger side. The values
are calculated from their place in the order of the rainbow. The value, or place is
the value d
in the functions.
Now we come to the graphics bit, actually drawing the lines. In SVG lines are called
paths. We use D3's .data()
function to draw one for each item in our
array:
var path = svgContainer .selectAll("g.arc") .data(order) .enter() .append("g") .attr("class", "arc");This creates a new
<g>
element for each item to hold our path. Lastly
we actually draw the animation:
path .append("path") .transition() .delay(function (d, i) { thisDelay = 0; for (let index = 0; index < i + 1; index++) { thisDelay += delay[index]; } return thisDelay; }) .duration(150) .attr("fill", function (d, i) { return colours[d]; }) .attr("d", arc);There's lots going on here, so we'll go from the top. First a
path
is added,
and we need to use a transition()
as it needs to be animated rather than all
at once. The delay()
function is used to draw the lines at different times.
The delay
array doesn't contain absolute values, so the loop adds up everything
that happens before this line. The duration()
instructs D3 to draw the line
over 150 milliseconds. The attr("fill")
call sets which colour will be used to draw
the line itself, before using the arc generator from before to set the attr("d")
or data for the path.