Skip to content

Instantly share code, notes, and snippets.

@fryford
Last active August 29, 2015 14:22
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fryford/2925ecf70ac9d9b51031 to your computer and use it in GitHub Desktop.
Save fryford/2925ecf70ac9d9b51031 to your computer and use it in GitHub Desktop.
Animating a single line & multiple lines

Animating a line

Whilst I wouldn't want to animate every line, it can be useful on certain occasions to illustrate growth / decline, direction or geographical movement. In this situation we're using population projections that can go in all sorts of different directions depending on the underlying assumptions made. The animatelines function below shows how to animate a line by modifying the stroke-dash & stroke-dashoffset properties of a line.

date PPP HPP LPP PHP PLP PPH PPL HHH LLL HLH LHL PP0 PHH PHL PLH PLL HPH HPL HHP HHL HLP HLL LPH LPL LHP LHH LLP LLH
2012 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705 63705
2013 64087 64087 64087 64087 64087 64097 64080 64097 64080 64097 64080 63920 63947 63930 63947 63930 63947 63930 63770 63797 63780 63797 63780 63797 63780 63620 63647 63630
2014 64511 64550 64476 64523 64499 64540 64487 64591 64441 64568 64464 64172 64390 64337 64441 64291 64418 64314 64022 64240 64187 64291 64141 64268 64164 63872 64090 64037
2015 64938 65032 64855 64961 64915 64995 64888 65112 64783 65066 64829 64422 64845 64738 64962 64633 64916 64679 64272 64695 64588 64812 64483 64766 64529 64122 64545 64438
2016 65386 65545 65246 65421 65352 65480 65301 65674 65127 65605 65196 64671 65330 65151 65524 64977 65455 65046 64521 65180 65001 65374 64827 65305 64896 64371 65030 64851
2017 65825 66054 65617 65872 65777 65965 65694 66242 65440 66147 65534 64915 65815 65544 66092 65290 65997 65384 64765 65665 65394 65942 65140 65847 65234 64615 65515 65244
2018 66266 66571 65984 66327 66206 66462 66080 66829 65739 66708 65860 65153 66312 65930 66679 65589 66558 65710 65003 66162 65780 66529 65439 66408 65560 64853 66012 65630
2019 66697 67081 66338 66773 66622 66960 66445 67421 66012 67270 66163 65384 66810 66295 67271 65862 67120 66013 65234 66660 66145 67121 65712 66970 65863 65084 66510 65995
2020 67126 67592 66685 67217 67034 67456 66806 68016 66277 67833 66460 65606 67306 66656 67866 66127 67683 66310 65456 67156 66506 67716 65977 67533 66160 65306 67006 66356
2021 67550 68100 67027 67659 67440 67950 67161 68613 66532 68393 66751 65817 67800 67011 68463 66382 68243 66601 65667 67650 66861 68313 66232 68093 66451 65517 67500 66711
2022 67969 68603 67362 68098 67839 68440 67509 69209 66777 68949 67036 66016 68290 67359 69059 66627 68799 66886 65866 68140 67209 68909 66477 68649 66736 65716 67990 67059
2023 68382 69102 67690 68533 68229 68926 67849 69803 67012 69499 67315 66203 68776 67699 69653 66862 69349 67165 66053 68626 67549 69503 66712 69199 67015 65903 68476 67399
2024 68788 69593 68013 68964 68610 69406 68181 70395 67236 70041 67589 66377 69256 68031 70245 67086 69891 67439 66227 69106 67881 70095 66936 69741 67289 66077 68956 67731
2025 69186 70076 68328 69389 68980 69880 68503 70983 67449 70573 67857 66537 69730 68353 70833 67299 70423 67707 66387 69580 68203 70683 67149 70273 67557 66237 69430 68053
2026 69575 70549 68634 69808 69338 70347 68815 71566 67649 71095 68118 66683 70197 68665 71416 67499 70945 67968 66533 70047 68515 71266 67349 70795 67818 66383 69897 68365
2027 69955 71013 68932 70221 69684 70805 69116 72144 67836 71605 68372 66814 70655 68966 71994 67686 71455 68222 66664 70505 68816 71844 67536 71305 68072 66514 70355 68666
2028 70325 71467 69220 70628 70016 71255 69407 72716 68009 72103 68619 66932 71105 69257 72566 67859 71953 68469 66782 70955 69107 72416 67709 71803 68319 66632 70805 68957
2029 70686 71911 69498 71028 70336 71697 69688 73284 68167 72589 68858 67036 71547 69538 73134 68017 72439 68708 66886 71397 69388 72984 67867 72289 68558 66736 71247 69238
2030 71037 72346 69766 71423 70642 72130 69957 73846 68311 73062 69090 67127 71980 69807 73696 68161 72912 68940 66977 71830 69657 73546 68011 72762 68790 66827 71680 69507
2031 71380 72772 70024 71812 70934 72555 70217 74403 68440 73522 69315 67206 72405 70067 74253 68290 73372 69165 67056 72255 69917 74103 68140 73222 69015 66906 72105 69767
2032 71713 73189 70273 72196 71214 72971 70467 74957 68555 73972 69534 67273 72821 70317 74807 68405 73822 69384 67123 72671 70167 74657 68255 73672 69234 66973 72521 70017
2033 72037 73600 70513 72576 71481 73379 70709 75509 68656 74410 69746 67330 73229 70559 75359 68506 74260 69596 67180 73079 70409 75209 68356 74110 69446 67030 72929 70259
2034 72355 74005 70743 72952 71736 73780 70942 76059 68744 74839 69954 67378 73630 70792 75909 68594 74689 69804 67228 73480 70642 75759 68444 74539 69654 67078 73330 70492
2035 72666 74405 70966 73325 71981 74176 71169 76610 68819 75260 70157 67418 74026 71019 76460 68669 75110 70007 67268 73876 70869 76310 68519 74960 69857 67118 73726 70719
2036 72971 74804 71181 73697 72216 74566 71390 77162 68883 75674 70356 67450 74416 71240 77012 68733 75524 70206 67300 74266 71090 76862 68583 75374 70056 67150 74116 70940
2037 73272 75200 71390 74068 72443 74952 71606 77717 68935 76083 70552 67477 74802 71456 77567 68785 75933 70402 67327 74652 71306 77417 68635 75783 70252 67177 74502 71156
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Animate a line</title>
<style>
body {
font-family: sans-serif;
}
a {
font-size:20px;
margin-right:30px;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line {
fill:none;
}
.x.axis path {
display: inline;
}
.y.axis path {
display: none;
}
.axis.y .tick line {
stroke-width:1px;
stroke:#ccc;
stroke-opacity:1;
}
</style>
<body>
<div id="controls">
<a href="javascript:;">Draw the first line</a>
<a href="javascript:;">Animate all the lines</a>
</div>
<div id="chart"> </div>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
document.addEventListener('DOMContentLoaded', function(){
// your code goes here
d3.selectAll("a").on("click",function(d,i){return animatelines(i);})
makeChart();
}, false);
function makeChart()
{
// Load in the data now...
d3.csv("data.csv", function(error, data) {
//Get width of page
var chartwidth = parseInt(d3.select("#chart").style("width"));
// Set the margins
var margin = {top: 20, right: 15, bottom: 30, left: 60},
width = chartwidth - margin.left - margin.right,
height = 430 - margin.top - margin.bottom;
// Set up the format to match that of the data that is being read in - have a look here for a list of formats - https://github.com/mbostock/d3/wiki/Time-Formatting
var parseDate = d3.time.format("%Y").parse;
// Setting up the scaling objects
var x = d3.time.scale()
.range([0, width]);
// Same for the y axis
var y = d3.scale.linear()
.range([height, 0]);
// Same for colour.
var color = d3.scale.category10();
//Setting x-axis up here using x scaling object
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
//Setting y-axis up here using y scaling object
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(-width);
// Setting up a d3 line object - used to draw lines later
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.population); });
// Now to actually make the chart area
var svg = d3.select("#chart").append("svg")
.attr("class", "svgele")
.attr("id", "svgEle")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
// Take each row and put the date column through the parsedate form we've defined above.
data.forEach(function(d) {
d.date = parseDate(d.date);
});
// Building an object with all the data in it for each line
projections = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, population: +d[name]};
})
};
});
// Set the domain of the x-value
x.domain(d3.extent(data, function(d) { return d.date; }));
// Do the same for the y-axis...[0,800000] by looking at the minimum and maximum for the population variable.
y.domain([
d3.min(projections, function(c) { return d3.min(c.values, function(v) { return v.population; }); }),
d3.max(projections, function(c) { return d3.max(c.values, function(v) { return v.population; }); })
]);
// Bind the x-axis to the svg object
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("y", 40)
.attr("x", width/2 ) //place the year label in the middle of the axis
.attr("dx", ".71em")
.style("text-anchor", "end")
.text("Year");
// append the yAxis and add label as before.
svg.append("g")
.attr("class", "y axis")
.attr("id", "#yAxis")
.call(yAxis)
.append("text")
.attr("y", 0)
.attr("x", -60)
.attr("dy", ".71em")
.style("text-anchor", "start")
.text("Population (Thousands)");
//create proj
var proj = svg.selectAll(".proj")
.data(projections)
.enter()
.append("g")
.attr("class", "proj");
// Drawing the lines
proj.append("path")
.attr("class", "line")
.attr("id" , function(d, i){
return "line" + i;
})
.attr("stroke-linecap","round")
.attr("d", function(d,i) {
return line(d.values);
})
.style("stroke", function(d) { return color(d.name); });
//Initially set the lines to not show
d3.selectAll(".line").style("opacity","0");
//Draw single line onload
animatelines(0);
});
}
function animatelines(whichline) {
// Look at what button was clicked
if(whichline == 0 ){
// First set all the lines to be invisible
d3.selectAll(".line").style("opacity","0");
// Then highlight the main line to be fully visable and give it a thicker stroke
d3.select("#line0").style("opacity","1").style("stroke-width",4);
// First work our the total length of the line
var totalLength = d3.select("#line0").node().getTotalLength();
d3.selectAll("#line0")
// Set the line pattern to be an long line followed by an equally long gap
.attr("stroke-dasharray", totalLength + " " + totalLength)
// Set the intial starting position so that only the gap is shown by offesetting by the total length of the line
.attr("stroke-dashoffset", totalLength)
// Then the following lines transition the line so that the gap is hidden...
.transition()
.duration(5000)
.ease("quad") //Try linear, quad, bounce... see other examples here - http://bl.ocks.org/hunzy/9929724
.attr("stroke-dashoffset", 0);
}
else if(whichline == 1){
d3.selectAll(".line").style("opacity","0.5");
//Select All of the lines and process them one by one
d3.selectAll(".line").each(function(d,i){
// Get the length of each line in turn
var totalLength = d3.select("#line" + i).node().getTotalLength();
d3.selectAll("#line" + i).attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(5000)
.delay(100*i)
.ease("quad") //Try linear, quad, bounce... see other examples here - http://bl.ocks.org/hunzy/9929724
.attr("stroke-dashoffset", 0)
.style("stroke-width",3)
})
}
}
</script>
</body>
</head>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment