a responsive bar chart. open in a new window ↗️
to enjoy the buttery responsiveness
forked from micahstubbs's block: responsive bar chart
Note the React-ish renderBars
function, using the d3 v4 approach to selections
function renderBars() {
const updateSelection = layers.barGroup.selectAll('rect').data(data);
const enterSelection = updateSelection.enter().append('rect');
const mergeSelection = updateSelection.merge(enterSelection);
const exitSelection = updateSelection.exit();
enterSelection
.classed('rect', true)
.style('fill', faintGray)
.on('mouseover', onBarActive)
.on('mouseout', onBarInactive);
mergeSelection
.transition().duration(150)
.attrs({
x: (d) => xScale(xValue(d)),
width: xScale.bandwidth,
y: (d) => yScale(yValue(d)),
height: (d) => yScale(0) - yScale(yValue(d))
});
exitSelection
.on('mouseover', null)
.on('mouseout', null)
.remove();
}
xxxxxxxxxx
<head>
<meta charset='utf-8'>
<script src='https://unpkg.com/pyrsmk-w/lib/w.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/d3@12.10.3/d3.min.js'></script>
<script src='https://unpkg.com/d3-selection-multi/build/d3-selection-multi.min.js'></script>
<script src='https://unpkg.com/babel-core@5/browser.min.js'></script>
</head>
<body>
<script>
const margin = { top: 10, right: 10, bottom: 25, left: 35 };
const maxWidth = 5120;
const maxHeight = 2880;
const xVariable = 'letter';
const yVariable = 'frequency';
const xValue = (d) => d[xVariable];
const yValue = (d) => d[yVariable];
const faintGray = '#ededed';
const xScale = d3.scaleBand();
const yScale = d3.scaleLinear();
const xAxis = d3.axisBottom().scale(xScale);
const yAxis = d3.axisLeft().scale(yScale);
const layers = defineLayers();
let data;
// main
W.addListener(render);
d3.select('body')
.styles({
margin: 0,
position: 'fixed',
top: 0,
right: 0,
bottom: 0,
left: 0
});
d3.csv('data.csv', rowParser, (error, response) => {
updateData(response);
render();
});
// main
function defineLayers() {
const svg = d3.select('body').append('svg')
.attrs({
width: '100%',
height: '100%'
});
const chartArea = svg.append('g')
.classed('chartArea', true)
.attr('transform', `translate(${margin.left}, ${margin.top})`);
const barGroup = chartArea.append('g')
.classed('bars', true);
const xAxisG = chartArea.append('g')
.classed('axis', true)
.classed('x', true);
const yAxisG = chartArea.append('g')
.classed('axis', true)
.classed('y', true);
return { svg, chartArea, barGroup, xAxisG, yAxisG };
}
function rowParser(d) {
// coerce to a Number from a String (or anything)
d[yVariable] = Number(d[yVariable]);
return d;
}
function updateData(newData) {
data = newData;
xScale.domain(data.map(xValue));
yScale.domain([0, d3.max(data.map(yValue))]);
}
function render() {
updateScales();
renderAxes();
renderBars();
}
function updateScales() {
const newWidth = d3.min([W.getViewportWidth(), maxWidth]) - margin.left - margin.right;
const newHeight = d3.min([W.getViewportHeight(), maxHeight]) - margin.top - margin.bottom;
xScale
.range([0, newWidth])
.paddingInner(0.1)
.bandwidth(10);
yScale.range([newHeight, 0]);
}
function renderAxes() {
const newHeight = d3.min([W.getViewportHeight(), maxHeight]);
layers.xAxisG
.transition().duration(150)
.attr('transform', `translate(0, ${newHeight - margin.top - margin.bottom})`)
.call(xAxis);
layers.yAxisG
.transition().duration(150)
.call(yAxis);
// style the axes
d3.selectAll('.axis text')
.styles({
'font-family': 'sans-serif',
'font-size': '10px'
});
d3.selectAll('.axis path')
.styles({
fill: 'none',
stroke: '#161616'
});
d3.selectAll('.axis line')
.styles({'stroke': 'black'});
}
function renderBars() {
const updateSelection = layers.barGroup.selectAll('rect').data(data);
const enterSelection = updateSelection.enter().append('rect');
const mergeSelection = updateSelection.merge(enterSelection);
const exitSelection = updateSelection.exit();
enterSelection
.classed('rect', true)
.style('fill', faintGray)
.on('mouseover', onBarActive)
.on('mouseout', onBarInactive);
mergeSelection
.transition().duration(150)
.attrs({
x: (d) => xScale(xValue(d)),
width: xScale.bandwidth,
y: (d) => yScale(yValue(d)),
height: (d) => yScale(0) - yScale(yValue(d))
});
exitSelection
.on('mouseover', null)
.on('mouseout', null)
.remove();
}
function onBarActive() {
d3.select(this)
.styles({
'fill': 'steelblue',
'fill-opacity': 0.6
});
}
function onBarInactive() {
d3.select(this)
.styles({
'fill': faintGray,
'fill-opacity': 1
});
}
</script>
</body>
Updated missing url https://unpkg.com/pyrsmk-w/lib/W.min.js to https://unpkg.com/pyrsmk-w/lib/w.min.js
Updated missing url https://unpkg.com/d3/build/d3.min.js to https://cdn.jsdelivr.net/npm/d3@12.10.3/d3.min.js
https://unpkg.com/pyrsmk-w/lib/W.min.js
https://unpkg.com/d3/build/d3.min.js
https://unpkg.com/d3-selection-multi/build/d3-selection-multi.min.js
https://unpkg.com/babel-core@5/browser.min.js