|
<!doctype html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<title>Document</title> |
|
<style> |
|
#refl { |
|
/* |
|
This is the 'correct' way to make the reflection, |
|
using the element() background technique: |
|
http://lea.verou.me/2011/06/css-reflections-for-firefox-with-moz-element-and-svg-masks/ |
|
Currently only works in moz though. |
|
*/ |
|
|
|
content: ""; |
|
display: block; |
|
width: 800px; |
|
height: 500px; |
|
margin: 0 auto; |
|
opacity: 0.1; |
|
z-index: -1; |
|
transform: translateY(-62px) scaleY(-1); |
|
background: -moz-linear-gradient(bottom, |
|
rgba(255,255,255,1) 0%, |
|
rgba(255,255,255,1) 5%, |
|
rgba(255,255,255,0) 6%, |
|
rgba(255,255,255,0) 45%, |
|
rgba(255,255,255,1) 75%, |
|
rgba(255,255,255,1) 100%), |
|
-moz-element(#chart); |
|
background-position: bottom, bottom; |
|
-moz-background-size: cover, cover; |
|
} |
|
body { |
|
font-family: helvetica, sans-seirf; |
|
} |
|
#chart { |
|
display: block; |
|
margin: 0 auto; |
|
/* |
|
This is the nonstandard way of making the reflection. |
|
Only works in webkit (the other way doesnt work in webkit so use this instead) |
|
*/ |
|
|
|
-webkit-box-reflect: below -60px |
|
-webkit-gradient(linear, left bottom, left top, |
|
color-stop(0.00, rgba(0,0,0,0)), |
|
color-stop(0.05, rgba(0,0,0,0)), |
|
color-stop(0.06, rgba(0,0,0,0.1)), |
|
color-stop(0.45, rgba(0,0,0,0.1)), |
|
color-stop(0.75, rgba(0,0,0,0)), |
|
color-stop(1.00, rgba(0,0,0,0))); |
|
} |
|
.x.axis g:first-of-type, .x.axis g:last-of-type { |
|
display: none; |
|
} |
|
.axis line, .axis path { |
|
fill: none; |
|
stroke: rgba(236, 60, 12, 0.6); |
|
shape-rendering: crispEdges; |
|
} |
|
.axis path.domain { |
|
stroke: none; |
|
} |
|
.axis text { |
|
fill: #8C807D; |
|
font-size: 1em; |
|
font-weight: 300; |
|
} |
|
.line { |
|
fill: none; |
|
stroke-width: 1px; |
|
stroke: rgba(236, 60, 12, 0.6); |
|
} |
|
.line.done { |
|
stroke: none; |
|
} |
|
.area { |
|
stroke-width: 0px; |
|
} |
|
.area1 { |
|
fill: rgba(245, 166, 189, 0.6); |
|
} |
|
.area2 { |
|
fill: rgba(236, 60, 12, 0.6); |
|
} |
|
.basline { |
|
stroke-width: 10px; |
|
} |
|
.area1Baseline { |
|
stroke: rgba(245, 166, 189, 0.6); |
|
} |
|
.area2Baseline { |
|
stroke: rgba(236, 60, 12, 0.6); |
|
} |
|
.axis line { |
|
transition: opacity 0.5s; |
|
} |
|
.axis.done line { |
|
opacity: 0; |
|
} |
|
|
|
#dribbble { |
|
position: fixed; |
|
bottom: 1em; |
|
width: 100%; |
|
font-family: 'Rambla', sans-serif; |
|
font-size: 2em; |
|
font-weight: bold; |
|
text-align: center; |
|
text-decoration: none; |
|
color: rgba(236, 60, 12, 0.6); |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<svg id="chart"></svg> |
|
<div id="refl"></div> |
|
<a id="dribbble" href="http://dribbble.com/shots/1215165-Infographic">D3 animation based on Jelio Dimitrov's Dribbble</a> |
|
<script src='//d3js.org/d3.v4.min.js'></script> |
|
<script> |
|
|
|
const d3 = window.d3 |
|
const parseDate = d3.timeParse("%d-%b-%Y") |
|
// d3.time.format("%d-%b-%Y").parse => d3.timeParse("%d-%b-%Y") |
|
|
|
const lineData = [ |
|
[parseDate("1-jan-2013"), 25], |
|
[parseDate("1-apr-2013"), 30], |
|
[parseDate("1-may-2013"), 50], |
|
[parseDate("1-jun-2013"), 60], |
|
[parseDate("1-dec-2013"), 45] |
|
] |
|
|
|
const intermediateLineData = [ |
|
[parseDate("1-jan-2013"), 0], |
|
[parseDate("1-apr-2013"), 5], |
|
[parseDate("1-may-2013"), 30], |
|
[parseDate("1-jun-2013"), 50], |
|
[parseDate("1-dec-2013"), 5] |
|
] |
|
|
|
const area1Data = [ |
|
[parseDate("1-dec-2012"), 0], |
|
[parseDate("1-jan-2013"), 25], |
|
[parseDate("1-feb-2013"), 10], |
|
[parseDate("1-mar-2013"), 17], |
|
[parseDate("1-apr-2013"), 30], |
|
[parseDate("1-may-2013"), 25], |
|
[parseDate("1-jun-2013"), 50], |
|
[parseDate("1-jul-2013"), 60], |
|
[parseDate("1-aug-2013"), 50], |
|
[parseDate("1-sep-2013"), 30], |
|
[parseDate("1-oct-2013"), 25], |
|
[parseDate("1-nov-2013"), 45], |
|
[parseDate("1-dec-2013"), 20], |
|
[parseDate("1-jan-2014"), 0] |
|
] |
|
|
|
const area2Data = [ |
|
[parseDate("1-dec-2012"), 0], |
|
[parseDate("1-jan-2013"), 10], |
|
[parseDate("1-feb-2013"), 7], |
|
[parseDate("1-mar-2013"), 12], |
|
[parseDate("1-apr-2013"), 25], |
|
[parseDate("1-may-2013"), 35], |
|
[parseDate("1-jun-2013"), 25], |
|
[parseDate("1-jul-2013"), 15], |
|
[parseDate("1-aug-2013"), 6], |
|
[parseDate("1-sep-2013"), 9], |
|
[parseDate("1-oct-2013"), 11], |
|
[parseDate("1-nov-2013"), 40], |
|
[parseDate("1-dec-2013"), 30], |
|
[parseDate("1-jan-2014"), 0] |
|
] |
|
|
|
|
|
const nullLineData = [ |
|
[parseDate("1-jan-2013"), 0], |
|
[parseDate("1-apr-2013"), 0], |
|
[parseDate("1-may-2013"), 0], |
|
[parseDate("1-jun-2013"), 0], |
|
[parseDate("1-dec-2013"), 0], |
|
] |
|
|
|
const extremeNullData = [ |
|
[parseDate("1-dec-2012"), 0], |
|
[parseDate("1-jan-2013"), 0], |
|
[parseDate("1-feb-2013"), 0], |
|
[parseDate("1-mar-2013"), 0], |
|
[parseDate("1-apr-2013"), 0], |
|
[parseDate("1-may-2013"), 0], |
|
[parseDate("1-jun-2013"), 0], |
|
[parseDate("1-jul-2013"), 0], |
|
[parseDate("1-aug-2013"), 0], |
|
[parseDate("1-sep-2013"), 0], |
|
[parseDate("1-oct-2013"), 0], |
|
[parseDate("1-nov-2013"), 0], |
|
[parseDate("1-dec-2013"), 0], |
|
[parseDate("1-jan-2014"), 0] |
|
] |
|
|
|
|
|
// Timing |
|
const start = 0 |
|
const beginChartIn = start + 800 //After baseline comes in |
|
const finishChartIn = beginChartIn + 1400 |
|
const beginChartOut = finishChartIn + 1500 |
|
const finishChartOut = beginChartOut + 1300 //begin taking baseline out |
|
const finish = finishChartOut + 1500 |
|
|
|
|
|
const marginBottom = 30 |
|
const width = 800 |
|
const height = 500 |
|
const chartBottom = height - marginBottom |
|
const chartHeight = chartBottom - 10; // 10 = baseline |
|
|
|
const svg = d3.select('#chart') |
|
.attr('width', width) |
|
.attr('height', height) |
|
|
|
// Scaling functions |
|
var xScale = d3.scaleTime () |
|
.range([0, width]) |
|
.domain(d3.extent(area1Data, d => d[0])) |
|
|
|
var yScale = d3.scaleLinear() |
|
.range([chartHeight, 0]) |
|
.domain([0, d3.max(area1Data, d => d[1] * 1.2)]) |
|
|
|
|
|
/* CREATE SVG ELEMENTS */ |
|
|
|
const xAxis = d3.axisBottom(xScale) |
|
.ticks(12) |
|
.tickFormat(d3.timeFormat("%b")) |
|
|
|
const axisPath = svg.append("g") |
|
.attr("class", "x axis done") |
|
.attr("transform", "translate(0," + chartBottom + ")") |
|
.call(xAxis); |
|
|
|
// Pink Area |
|
const area1 = d3.area() |
|
.x(d => xScale(d[0])) |
|
.y0(chartHeight) |
|
.y1(d => yScale(d[1])) |
|
|
|
const area1Path = svg.append("path") |
|
.attr("class", "area area1") |
|
|
|
const area1LineR = svg.append("line") |
|
.attr("class", "basline area1Baseline rightBaseline") |
|
|
|
const area1LineL = svg.append("line") |
|
.attr("class", "basline area1Baseline leftBaseline") |
|
|
|
// Orange Area |
|
const area2 = d3.area() |
|
.x(d => xScale(d[0])) |
|
.y0(chartHeight) |
|
.y1(d => yScale(d[1])) |
|
|
|
const area2Path = svg.append("path") |
|
.attr("class", "area area2") |
|
|
|
const area2LineR = svg.append("line") |
|
.attr("class", "basline area2Baseline rightBaseline") |
|
|
|
const area2LineL = svg.append("line") |
|
.attr("class", "basline area2Baseline leftBaseline") |
|
|
|
|
|
// Line Graph |
|
const line = d3.line() |
|
.x((d,i) => xScale(d[0])) |
|
.y((d,i) => yScale(d[1])) |
|
|
|
const linePath = svg.append("svg:path") |
|
.attr("class", "line") |
|
|
|
// Line that covers whole area to stop moz element reflection from thinking that the svg is shrinking when the graph shrinks. |
|
svg.append("line") |
|
.attr("x1", 0) |
|
.attr("y1", 0) |
|
.attr("x2", width) |
|
.attr("y2", height) |
|
.style("stroke", "blue") |
|
.style("stroke-width", "5px") |
|
.style("opacity", "0") |
|
|
|
|
|
|
|
/* |
|
THE FOLLOWING FUNCTION ACTUALLY PREFORMS THE ANIMATION |
|
CALL ON A LOOP TO MAKE THE ANIMATION LOOP |
|
*/ |
|
|
|
const beginAnimation = () => { |
|
|
|
area1Path |
|
.attr("d", area1(extremeNullData)) |
|
.transition() |
|
.delay(beginChartIn + 200) |
|
.duration(700) |
|
.ease(d3.easeElastic, 1, 0.9) |
|
.attr("d", area1(area1Data)) |
|
.transition() |
|
.delay(beginChartOut + 300) |
|
.duration(500) |
|
.ease(d3.easeCubic) |
|
.attr("d", area1(extremeNullData)); |
|
|
|
area1LineR |
|
.attr("x1", (width/2)) |
|
.attr("x2", (width/2)) |
|
.attr("y1", (height-marginBottom-5)) |
|
.attr("y2", (height-marginBottom-5)) |
|
.transition() |
|
.delay(start) |
|
.duration(500) |
|
.attr("x1", 0) |
|
.transition() |
|
.delay(finishChartOut+300) |
|
.duration(500) |
|
.attr("x1", (width/2)) |
|
|
|
area1LineL |
|
.attr("x1", (width/2)) |
|
.attr("x2", (width/2)) |
|
.attr("y1", (height-marginBottom-5)) |
|
.attr("y2", (height-marginBottom-5)) |
|
.transition() |
|
.delay(start) |
|
.duration(500) |
|
.attr("x2", width) |
|
.transition() |
|
.delay(finishChartOut+300) |
|
.duration(500) |
|
.attr("x2", (width/2)) |
|
|
|
|
|
area2Path |
|
.attr("d", area2(extremeNullData)) |
|
.transition() |
|
.delay(beginChartIn + 200) |
|
.duration(700) |
|
.ease(d3.easeElastic, 1, 0.9) |
|
.attr("d", area2(area2Data)) |
|
.transition() |
|
.delay(beginChartOut) |
|
.duration(500) |
|
.ease(d3.easeCubic) |
|
.attr("d", area2(extremeNullData)) |
|
|
|
area2LineR |
|
.attr("x1", (width/2)) |
|
.attr("x2", (width/2)) |
|
.attr("y1", (height-marginBottom-5)) |
|
.attr("y2", (height-marginBottom-5)) |
|
.transition() |
|
.delay(start + 300) |
|
.duration(500) |
|
.attr("x1", 0) |
|
.transition() |
|
.delay(finishChartOut) |
|
.duration(500) |
|
.attr("x1", (width/2)) |
|
|
|
area2LineL |
|
.attr("x1", (width/2)) |
|
.attr("x2", (width/2)) |
|
.attr("y1", (height-marginBottom-5)) |
|
.attr("y2", (height-marginBottom-5)) |
|
.transition() |
|
.delay(start + 300) |
|
.duration(500) |
|
.attr("x2", width) |
|
.transition() |
|
.delay(finishChartOut) |
|
.duration(500) |
|
.attr("x2", (width/2)); |
|
|
|
linePath |
|
.attr("d", line(nullLineData)) |
|
.transition() |
|
.delay(beginChartIn+200) |
|
.duration(600) |
|
.ease(d3.easeLinear, 1, 0.4) |
|
.attr("d", line(intermediateLineData)) |
|
.attr("class", "line") |
|
.transition() |
|
.delay(beginChartIn+800) |
|
.duration(600) |
|
.ease(d3.easeElastic, 1, 0.4) |
|
.attr("d", line(lineData)) |
|
.transition() |
|
.delay(beginChartOut+600) |
|
.duration(500) |
|
.ease(d3.easeCubic) |
|
.attr("d", line(nullLineData)) |
|
|
|
.transition() |
|
.delay(finishChartOut) |
|
.attr("class", "line done") |
|
|
|
axisPath |
|
.transition() |
|
.delay(start) |
|
.duration(500) |
|
.attr("class", "x axis") |
|
.transition() |
|
.delay(finishChartOut+800) |
|
.duration(500) |
|
.attr("class", "x axis done") |
|
|
|
} |
|
|
|
beginAnimation() |
|
setInterval(beginAnimation, finish) |
|
</script> |
|
</body> |
|
</html> |