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.
Last active
August 29, 2015 14:22
-
-
Save fryford/2925ecf70ac9d9b51031 to your computer and use it in GitHub Desktop.
Animating a single line & multiple lines
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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