Fun with D3 animation
A Pen by Siddharth Parmar on CodePen.
Fun with D3 animation
A Pen by Siddharth Parmar on CodePen.
<div class="chart-wrapper"></div> | |
<button onclick='replay()'>Replay</button> |
var dataset = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; | |
// var colors = ['#8dd3c7', '#ffffb3', '#bebada', '#fb8072', '#80b1d3', '#fdb462', '#b3de69', '#fccde5', '#d9d9d9', '#bc80bd']; | |
// var colors = ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#e0e0e0', '#bababa', '#878787', '#4d4d4d', '#1a1a1a']; | |
var colors = ['#9e0142', '#d53e4f', '#f46d43', '#fdae61', '#fee08b', '#e6f598', '#abdda4', '#66c2a5', '#3288bd', '#5e4fa2']; | |
var width = document.querySelector('.chart-wrapper').offsetWidth; | |
var height = document.querySelector('.chart-wrapper').offsetHeight; | |
var minOfWH = Math.min(width, height) / 2; | |
var initialAnimDelay = 500; | |
var arcAnimDelay = 200; | |
var arcAnimDur = 3000; | |
var radius; | |
// calculate minimum of width and height to set chart radius | |
if (minOfWH > 200) { | |
radius = 200; | |
} else { | |
radius = minOfWH; | |
} | |
// append svg | |
var svg = d3.select('.chart-wrapper').append('svg') | |
.attr({ | |
'width': width, | |
'height': height, | |
'class': 'pieChart' | |
}) | |
.append('g'); | |
svg.attr({ | |
'transform': 'translate(' + width / 2 + ',' + height / 2 + ')' | |
}); | |
// for drawing slices | |
var arc = d3.svg.arc() | |
.outerRadius(radius * 0.6) | |
.innerRadius(radius * 0.45); | |
// for labels and polylines | |
var outerArc = d3.svg.arc() | |
.innerRadius(radius * 0.85) | |
.outerRadius(radius * 0.85); | |
// d3 color generator | |
var c10 = d3.scale.category10(); | |
var pie = d3.layout.pie() | |
.value(function(d) { | |
return d; | |
}); | |
var draw = function() { | |
svg.append("g") | |
.attr("class", "lines"); | |
svg.append("g") | |
.attr("class", "slices"); | |
svg.append("g") | |
.attr("class", "labels"); | |
// define slice | |
var slice = svg.select('.slices') | |
.datum(dataset) | |
.selectAll('path') | |
.data(pie); | |
slice | |
.enter().append('path') | |
.attr({ | |
'fill': function(d, i) { | |
// slice color | |
return colors[i]; | |
}, | |
'd': arc, | |
'stroke-width': '25px' | |
}) | |
.attr('transform', function(d, i) { | |
return 'rotate(-180, 0, 0)'; | |
}) | |
.style('opacity', 0) | |
.transition() | |
.delay(function(d, i) { | |
return (i * arcAnimDelay) + initialAnimDelay; | |
}) | |
.duration(arcAnimDur) | |
.ease('elastic') | |
.style('opacity', 1) | |
.attr({ | |
'transform': 'rotate(0,0,0)', | |
'stroke-width': '5px' | |
}); | |
function midAngle(d) { | |
return d.startAngle + (d.endAngle - d.startAngle) / 2; | |
} | |
var text = svg.select(".labels").selectAll("text") | |
.data(pie(dataset)); | |
text.enter() | |
.append('text') | |
.attr('dy', '0.35em') | |
.style("opacity", 0) | |
.style('fill', function(d, i) { | |
return colors[i]; | |
}) | |
.text(function(d, i) { | |
return colors[i]; | |
}) | |
.attr('transform', function(d) { | |
// calculate outerArc centroid for 'this' slice | |
var pos = outerArc.centroid(d); | |
// define left and right alignment of text labels | |
pos[0] = radius * (midAngle(d) < Math.PI ? 1 : -1); | |
return "translate(" + pos + ")"; | |
}) | |
.style('text-anchor', function(d) { | |
return midAngle(d) < Math.PI ? "start" : "end"; | |
}) | |
.transition() | |
.delay(function(d, i) { | |
return arcAnimDur + (i * 250); | |
}) | |
.duration(1000) | |
.style('opacity', 1); | |
var polyline = svg.select(".lines").selectAll("polyline") | |
.data(pie(dataset)); | |
polyline.enter() | |
.append("polyline") | |
.style("opacity", 0) | |
.attr('points', function(d) { | |
var pos = outerArc.centroid(d); | |
pos[0] = radius * 0.95 * (midAngle(d) < Math.PI ? 1 : -1); | |
return [arc.centroid(d), arc.centroid(d), arc.centroid(d)]; | |
}) | |
.transition() | |
.delay(function(d, i) { | |
return i * 250; | |
}) | |
.duration(1000) | |
.delay(function(d, i) { | |
return arcAnimDur + (i * 250); | |
}) | |
.attr('points', function(d) { | |
var pos = outerArc.centroid(d); | |
pos[0] = radius * 0.95 * (midAngle(d) < Math.PI ? 1 : -1); | |
return [arc.centroid(d), outerArc.centroid(d), pos]; | |
}) | |
.style('opacity', 0.5); | |
}; | |
draw(); | |
button = document.querySelector('button'); | |
var replay = function() { | |
d3.selectAll('.slices').transition().ease('back').duration(500).delay(0).style('opacity', 0).attr('transform', 'translate(0, 250)').remove(); | |
d3.selectAll('.lines').transition().ease('back').duration(500).delay(100).style('opacity', 0).attr('transform', 'translate(0, 250)').remove(); | |
d3.selectAll('.labels').transition().ease('back').duration(500).delay(200).style('opacity', 0).attr('transform', 'translate(0, 250)').remove(); | |
setTimeout(draw, 800); | |
} |
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> |
$primColor: #0d0d0d; | |
$secColor: #fff; | |
body { | |
overflow: hidden; | |
font-size: 16px; | |
} | |
.chart-wrapper { | |
width: 100%; | |
height: 100%; | |
background-color: $primColor; | |
position: absolute; | |
} | |
path { | |
stroke: $primColor; | |
/* stroke-width: 5px; */ | |
cursor: pointer; | |
transition: stroke-width 250ms,fill 250ms; | |
&:hover { | |
/* stroke-width: 10px; */ | |
fill: $secColor; | |
} | |
} | |
text { | |
font-size: .8em; | |
text-transform: uppercase; | |
letter-spacing: .5px; | |
} | |
polyline { | |
fill: none; | |
stroke: $secColor; | |
stroke-width: 2px; | |
stroke-dasharray: 5px; | |
} | |
button { | |
position: absolute; | |
top: 20px; | |
left: 20px; | |
text-transform: uppercase; | |
padding: 5px 10px; | |
outline: none; | |
font-size: .6em; | |
background-color: transparent; | |
color: $secColor; | |
border: 1px solid $secColor; | |
letter-spacing: 1px; | |
transition: all 250ms; | |
&:hover { | |
background-color: $secColor; | |
color: $primColor; | |
box-shadow: 0 0 2px $secColor; | |
} | |
&:active { | |
opacity: 0.5; | |
} | |
} |