Skip to content

Instantly share code, notes, and snippets.

@ptvans
Created November 19, 2015 22:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ptvans/c53fb28a9991a8c001ed to your computer and use it in GitHub Desktop.
Save ptvans/c53fb28a9991a8c001ed to your computer and use it in GitHub Desktop.
Interpolation and Area Charts

Interpolation/Easing with Randomized Data

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;
}
@AneelGunji
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment