An area chart with randomized data used to expirement with interpolations and easing functions.
xxxxxxxxxx
<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>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js