An area chart with randomized data used to expirement with interpolations and easing functions.
forked from matt81m's block: Interpolation and Area Charts
An area chart with randomized data used to expirement with interpolations and easing functions.
forked from matt81m's block: Interpolation and Area Charts
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
svg { width: 100%; height: 100%; } | |
</style> | |
<link href="styles.css" rel="stylesheet" type="text/css" /> | |
</head> | |
<body> | |
<div class="button-container"> | |
<button onclick="changeData()"> | |
Update Data | |
</button> | |
<p>Interpolator</p> | |
<button class="interpolate-button" onclick="changeInterpolation()"></button> | |
<div class="ease-container"> | |
<p>Easing Function</p> | |
<button class="ease-button" onclick="changeEasing()"></button> | |
<div class="radio"> | |
<input name="ease-type" class="ease-suffix" type="radio" value="in"> | |
<label>in</label> | |
<input name="ease-type" class="ease-suffix" type="radio" value="out"> | |
<label>out</label> | |
<input name="ease-type" class="ease-suffix" type="radio" value="in-out"> | |
<label>in-out</label> | |
</div> | |
</div> | |
<p>Duration</p> | |
<input type="range" class="duration" value="400" min="0" max="1200"/> | |
<label class="dur"></label> | |
</div> | |
<figure> | |
<svg></svg> | |
</figure> | |
<script> | |
var eases = ['linear', 'poly(2)', 'cubic', 'sin', 'exp', 'circle', 'elastic', 'back', 'bounce']; | |
var interps = ['linear', 'linear-closed', 'step', 'step-before', 'step-after', 'basis', 'basis-open', 'basis-closed', 'bundle', 'cardinal', 'cardinal-open', 'cardinal-closed', 'monotone']; | |
var ease = 'back', | |
easeCount = eases.indexOf(ease), | |
interp = 'cardinal', | |
interpCount = interps.indexOf(interp), | |
duration = 600; | |
// tooltip dimensions | |
var tt ={ w: 80, h: 50 }; | |
// generate array of random numbers | |
function generate(min, max) { | |
var arr = []; | |
for (var i = 0; i < 6; i++) { | |
var rand = Math.random() * (max-min) + min; | |
var rounded = Math.round(rand * 100)/100; | |
arr.push(rounded); | |
} | |
// add endpoints | |
var startPoint = arr[0] - 0.2; | |
var endPoint = arr[arr.length - 1] - 0.2; | |
arr.unshift(startPoint); | |
arr.push(endPoint); | |
return arr; | |
}; | |
var margin = { | |
top: 20, | |
right: 40, | |
bottom: 40, | |
left: 40 | |
} | |
var data = [ | |
{ | |
name: 'state', | |
bg: '#C2E3F9', | |
stroke: '#0362AD', | |
opacity: 0.5, | |
data: [2.2, 2.4, 3.9, 4.6, 2.19, 4.5, 3.45, 3.25] | |
}, | |
{ | |
name: 'national', | |
bg: '#F7CDCD', | |
stroke: '#D9534F', | |
opacity: 0.5, | |
data: [3.6, 3.8, 3.15, 3.4, 2.93, 4.3, 4.1, 3.9] | |
}, | |
{ | |
name: 'facility', | |
bg: '#F9E0B2', | |
stroke: '#F0A611', | |
opacity: 0.84, | |
data: [2.8, 3, 3.5, 4.2, 2.35, 4.8, 2.9, 2.7] | |
}, | |
] | |
// ** Updates ** // | |
// update data | |
function changeData() { | |
for (var obj in data) { | |
var updated = generate(2, 4.8); | |
data[obj]['data'] = updated; | |
} | |
update(); | |
} | |
// change easing | |
function changeEasing() { | |
d3.selectAll('.ease-suffix').property('checked', false); | |
easeCount = ++easeCount % eases.length; | |
ease = eases[easeCount]; | |
d3.select('.ease-button').html(ease); | |
update(); | |
} | |
// easing suffix | |
d3.selectAll('.ease-suffix').on('click', function() { | |
console.info(this.value); | |
ease = eases[easeCount] + '-' + this.value; | |
d3.select('.ease-button').html(ease); | |
update(); | |
console.info(ease); | |
}); | |
// change interpolation | |
function changeInterpolation() { | |
interpCount = ++interpCount % interps.length; | |
interp = interps[interpCount]; | |
d3.select('.interpolate-button').html(interp); | |
update(); | |
} | |
d3.select('.duration').on('input', function() { | |
duration = this.value; | |
d3.select('.dur').html(duration + 'ms'); | |
update(); | |
}) | |
// ** DOM Elements ** // | |
d3.select('.ease-button').html(ease); | |
d3.select('.interpolate-button').html(interp); | |
d3.select('.dur').html(duration + ' ms'); | |
var w = d3.select('figure').node().clientWidth - margin.left - margin.right; | |
var h = d3.select('figure').node().clientHeight - margin.top - margin.bottom; | |
var x = d3.scale.ordinal() | |
.domain([0,1,2,3,4,5,6, 7]) | |
.rangePoints([0, w], -1.5); | |
var y = d3.scale.linear() | |
.domain([0, 5]) | |
.range([h, 0]); | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient('bottom') | |
.tickPadding([20]) | |
.tickSize(0, 0) | |
.tickValues([1,2,3,4,5,6]); | |
var yAxis = d3.svg.axis() | |
.scale(y) | |
.orient('left') | |
.tickSize(-(w), 0) | |
.tickPadding([20]) | |
.tickFormat(d3.format('r')) | |
.tickValues([1, 2, 3, 4, 5]); | |
// Define the div for the tooltip | |
var tooltip = d3.select('figure').append('div') | |
.classed('tooltip', true) | |
.style({ | |
opacity: 0, | |
width: tt.w + 'px', | |
height: tt.h + 'px' | |
}); | |
// Tooltip Text | |
tooltip.append('div') | |
.classed('text', true); | |
// Triangle after tooltip | |
tooltip.append('div') | |
.classed('triangle', true) | |
.style({ | |
bottom: ( (tt.h/4) * -1 ) + 'px', | |
left: (tt.w / 2) - (tt.h/3) + 'px', | |
'border-top': (tt.h/3) + 'px solid white', | |
'border-right': (tt.h/3) + 'px solid transparent', | |
'border-left': (tt.h/3) + 'px solid transparent' | |
}); | |
var clip = d3.select('svg').append('defs').append('clipPath') | |
.attr('id', 'clip') | |
.append('rect') | |
.attr({ | |
id: 'clip-rect', | |
x: 0, | |
y: 0, | |
width: w, | |
height: h | |
}); | |
var svg = d3.select('svg') | |
.attr('width', w + margin.left + margin.right) | |
.attr('height', h + margin.top + margin.bottom); | |
// X Axis | |
svg.append('g') | |
.attr('class', 'x axis') | |
.attr('transform', 'translate(' + margin.left + ',' + parseInt(h + margin.top) + ')') | |
.call(xAxis) | |
// Y Axis | |
svg.append('g') | |
.attr('class', 'y axis') | |
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') | |
.call(yAxis); | |
var g = svg.append('g') | |
.attr('clip-path', 'url(#clip)') | |
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); | |
var area = d3.svg.area() | |
.interpolate(interp) | |
.x(function(d, i) { return x(i); }) | |
.y0(h) | |
.y1(function(d) { return y(d); }); | |
var stacks = g.selectAll('.stacks') | |
.data(data) | |
.enter().append('g') | |
.classed('stacks', true); | |
stacks.append('path') | |
.attr({ | |
fill: function(d) { return d.bg; }, | |
stroke: function(d) { return d.stroke; }, | |
'stroke-width': 2, | |
opacity: function(d) { return d.opacity; } | |
}) | |
.attr('d', function(d) { return area(d.data) }); | |
var lines = stacks.selectAll('line') | |
.data(function(d) { return d.data; }) | |
.enter().append('line') | |
.attr({ | |
x1: function(d,i) { | |
return x(i) | |
}, | |
y1: h, | |
x2: function(d,i) { | |
return x(i) | |
}, | |
y2: function(d) { | |
return y(d) | |
} | |
}) | |
.each(function(d, i) { | |
var parent = this.parentNode.__data__; | |
d3.select(this) | |
.attr({ | |
stroke: parent.stroke, | |
'stroke-width': 2, | |
opacity: function(d, i) { | |
if (parent.name === 'facility') { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
}); | |
}); | |
var circles = stacks.selectAll('circle') | |
.data(function(d) { return d.data; }) | |
.enter().append('circle') | |
.attr({ | |
r: 10, | |
'stroke-width': 2, | |
cx: function(d, i) { | |
return x(i); | |
}, | |
cy: function(d) { | |
return y(d); | |
}, | |
opacity: function(d, i) { | |
if (!i || i === data[0].data.length -1) { | |
return 0; | |
} else { | |
return 1; | |
} | |
} | |
}) | |
.each(function() { | |
var parent = this.parentNode.__data__; | |
this._parentAttrs = parent; | |
d3.select(this) | |
.attr({ | |
fill: parent.bg, | |
stroke: parent.stroke, | |
'stroke-width': 2 | |
}); | |
}); | |
// Tooltip on hover | |
circles | |
.on('mousemove', function() { | |
d3.select(this).transition().duration(50).attr('r', 13); | |
var parent = this._parentAttrs; | |
var text = d3.select(this).data()[0]; | |
tooltip.select('.text').html(text); | |
var locX = d3.event.pageX - (tt.w / 2); | |
var locY = d3.event.pageY - ( tt.h + (tt.h/1.3) ); | |
tooltip.style({ | |
opacity: 1, | |
left: locX + 'px', | |
top: locY + 'px', | |
'background-color': parent.stroke | |
}); | |
tooltip.select('.triangle').style({ | |
'border-top-color': parent.stroke | |
}); | |
}) | |
.on('mouseleave', function() { | |
d3.select(this).transition().duration(50).attr('r', 10); | |
tooltip.style({ | |
opacity: 0, | |
left: 0, | |
right: 0 | |
}); | |
}); | |
function update() { | |
area.interpolate(interp); | |
stacks = stacks.data(data); | |
stacks.select('path') | |
.transition() | |
.ease(ease) | |
.duration(duration) | |
.attr('d', function(d) { return area(d.data); }); | |
circles = stacks.selectAll('circle') | |
.data(function(d) { return d.data; }); | |
circles.transition() | |
.duration(duration) | |
.ease(ease) | |
.attr({ | |
cx: function(d, i) { return x(i); }, | |
cy: function(d) { return y(d); } | |
}); | |
lines = stacks.selectAll('line') | |
.data(function(d) { return d.data; }); | |
lines.transition() | |
.duration(duration) | |
.ease(ease) | |
.attr({ | |
x1: function(d,i) { | |
return x(i) | |
}, | |
y1: h, | |
x2: function(d,i) { | |
return x(i) | |
}, | |
y2: function(d) { | |
return y(d) | |
} | |
}) | |
} | |
</script> | |
</body> | |
* { | |
box-sizing: border-box; | |
padding: 0; | |
margin: 0; | |
} | |
body { | |
font-family: sans-serif; | |
} | |
.button-container { | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
} | |
p { | |
font-size: 12px; | |
color: #a0a0a0; | |
display: inline; | |
} | |
button { | |
position: relative; | |
padding: 1em; | |
margin: 2em 4em 2em 1em; | |
width: 9em; | |
} | |
.ease-container { | |
position: relative; | |
text-align: center; | |
} | |
.radio { | |
position: absolute; | |
right: 0; | |
bottom: 0.5em; | |
text-align: center; | |
margin-right: 3.5em; | |
bottom: 0; | |
font-size: 11px; | |
} | |
input[type='range'] { | |
margin: 2em; | |
width: 14em; | |
} | |
label { | |
margin-right: 0.5em; | |
color: #a0a0a0; | |
font-size: 80%; | |
} | |
figure { | |
margin: auto; | |
width: 900px; | |
height: 410px; | |
} | |
circle { | |
cursor: pointer; | |
} | |
.axis text { | |
fill: #a0a0a0; | |
font: 14px 'Arial'; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #d8d8d8; | |
stroke-width: 2; | |
shape-rendering: crispEdges; | |
} | |
.y line { | |
stroke: #d8d8d8; | |
stroke-width: 2; | |
stroke-opacity: 0.5; | |
} | |
.tooltip { | |
position: absolute; | |
text-align: center; | |
} | |
.text { | |
font-family: 'Oswald'; | |
font-weight: 400; | |
font-size: 24px; | |
margin-top: 0.25em; | |
color: white; | |
width: 100%; | |
height: 100%; | |
} | |
.triangle { | |
position: absolute; | |
} |
Hi i want your help in this chart i want to make it dynamic and x-axis will months and y-axis is values
that Y-Axis values updates should be in dynamic based on the dataset that we took.