Built with blockbuilder.org
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
.axis path, .axis line {
stroke: #ccc;
}
.axis line {
stroke-dasharray: 2;
}
.axis text {
fill: #ccc;
}
.state text {
font-weight: 600;
}
</style>
</head>
<body>
<script>
var width = 250;
var height = 200;
var margin = {top: 20, right: 30, bottom: 20, left: 30};
var xScale = d3.scaleLinear().range([0, width]);
var yScale = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5)
.tickFormat(d => "'" + new String(d).slice(2))
.tickSizeInner(-height)
.tickSizeOuter(0)
.tickPadding(6);
var yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5)
.tickFormat(d => d + '%')
.tickSizeInner(-width)
.tickSizeOuter(0)
.tickPadding(6);
var line = d3.line()
.x(d => xScale(d.year))
.y(d => yScale(d.rate));
var red = '#d8472b';
var dispatch = d3.dispatch('load', 'yearchange');
d3.json('data.json', function(err, data) {
var flatData = [];
data.forEach(obj => {
var year = +obj.date;
for (key in obj) {
if (key !== 'date' && key !== 'avg' && key !== 'D.C.') {
var state = key;
var rate = obj[key];
flatData.push({
year: year,
state: state,
rate: +rate,
});
}
}
});
// get the domain for scales
var xDomain = d3.extent(flatData, d => d.year);
var yDomain = d3.extent(flatData, d => d.rate);
xScale.domain(xDomain);
yScale.domain(yDomain).nice();
var dataByState = d3.nest()
.key(d => d.state)
.entries(flatData);
// create svg for each state
var svg = d3.select('body')
.selectAll('svg')
.data(dataByState, d => d.state)
.enter().append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + [margin.left, margin.top] + ')');
dispatch.call('load', null, svg, dataByState);
// default hovered year to 2016
dispatch.call('yearchange', null, 2016);
});
// draw the static graph for each state
dispatch.on('load.graph', (svg, dataByState) => {
// draw axes
svg.append('g')
.classed('x axis', true)
.attr('transform', 'translate(' + [0, height] + ')')
.call(xAxis);
svg.append('g')
.classed('y axis', true)
.call(yAxis);
// draw lines
svg.append('path')
.datum(d => d.values)
.attr('d', line)
.attr('stroke', red)
.attr('stroke-width', 2)
.attr('fill', 'none');
svg.append('rect')
.attr('width', width)
.attr('height', height)
.attr('opacity',0)
.on('mousemove',function(){
var [x, y] = d3.mouse(this);
var year = Math.round(xScale.invert(x));
dispatch.call('yearchange', null, year);
});
});
dispatch.on('load.title', (svg) => {
var title = svg.append('text');
dispatch.on('yearchange.title', year => {
title.text(d => {
var rate = d.values.find(d => d.year === year);
rate = rate ? d3.format('.1f')(rate.rate) + '%' : 'N/A';
return d.key + ' (' + rate + ')';
})
});
});
dispatch.on('load.point', svg => {
var g = svg.append('g');
g.append('circle')
.attr('fill', red)
.attr('r', 5);
g.append('text')
.attr('text-anchor', 'middle')
.attr('font-size', 11);
dispatch.on('yearchange.point', year => {
g.attr('transform', d => {
var rate = d.values.find(d => d.year === year);
return 'translate(' + [xScale(rate.year), yScale(rate.rate)] + ')';
});
g.select('text')
.text(d => {
var rate = d.values.find(d => d.year === year);
return rate.year + ' (' + d3.format('.1f')(rate.rate) + '%)';
});
});
});
</script>
</body>
https://d3js.org/d3.v4.min.js