chart = {
const svg = d3.create('svg')
.attr('viewBox', [0, 0, width, noSplitHeight + margin.top + margin.bottom]);
const wrapper = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
wrapper.append('g')
.call(xAxis);
const medianLine = wrapper.append('line')
.attr('x1', x(median))
.attr('x2', x(median))
.attr('y1', 10)
.attr('y2', noSplitHeight)
.attr('stroke', '#ccc');
const medianText = wrapper.append('text')
.attr('x', x(median) + 5)
.attr('y', 25)
.attr('font-size', '11px')
.text('Median speed');
const yAxisContainer = wrapper.append('g')
.attr('transform', `translate(-10,0)`);
const circles = wrapper.append('g')
.attr('className', 'circles')
.selectAll('circle')
.data(running)
.join('circle')
.attr('r', d => r(d.distance))
.attr('fill', d => color(d.speed))
.attr('x', d => x(d.speed))
.attr('y', d => y(d.year) + y.bandwidth() / 2)
force.on('tick', () => {
circles
.transition()
.ease(d3.easeLinear)
.attr('cx', d => d.x)
.attr('cy', d => d.y);
})
invalidation.then(() => force.stop());
return Object.assign(svg.node(), {
update(split) {
let height = split ? splitHeight : noSplitHeight;
let years = [...yearGroups.keys()].sort()
const t = d3.transition().duration(750);
svg.transition(t).attr('viewBox', [0, 0, width, height]);
y.domain(split ? years : ['All']);
y.range(split ? [splitHeight - margin.top - margin.bottom, 0] : [noSplitHeight - margin.top - margin.bottom, 0]);
yAxisContainer.call(yAxis, y, split ? years : ['All'])
.call(g => g.select('.domain').remove())
.call(g => g.selectAll('.tick line').remove());
force.force('y', split ? d3.forceY(d => y(d.year) + y.bandwidth() / 2) :
d3.forceY((noSplitHeight - margin.top - margin.bottom) / 2));
force.alpha(1).restart();
medianLine.transition(t).attr('y2', split ? splitHeight - 20 : noSplitHeight);
}
});
}