Uses d3-area-label to position labels on a StreamGraph.
Inspired by Noah Veltman’s Block: Stacked area label placement
Built with blockbuilder.org
license: mit |
Uses d3-area-label to position labels on a StreamGraph.
Inspired by Noah Veltman’s Block: Stacked area label placement
Built with blockbuilder.org
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<script src="https://unpkg.com/d3@4.10.0"></script> | |
<script src="https://unpkg.com/d3-area-label@1.0.0"></script> | |
<title>Area Label Test</title> | |
<style> | |
.area-label { | |
font-family: sans-serif; | |
fill-opacity: 0.7; | |
fill: white; | |
} | |
path { | |
fill-opacity: 0.6; | |
} | |
</style> | |
</head> | |
<body> | |
<svg width="960" height="500"></svg> | |
<script> | |
const generateData = keys => { | |
const n = 100 | |
const prev = {} | |
const velocity = {} | |
const data = d3.range(n).map((d, i) => { | |
const row = { time: i } | |
keys.forEach(key => { | |
velocity[key] = ((velocity[key] || 0) + (Math.random() - .5)) * 0.9 | |
const value = Math.max(0.2, (prev[key] || Math.random() * 10) + velocity[key]) | |
prev[key] = row[key] = value | |
}) | |
return row | |
}) | |
data.keys = keys | |
return data | |
} | |
const svg = d3.select('svg') | |
const width = +svg.attr('width') | |
const height = +svg.attr('height') | |
const stack = d3.stack().offset(d3.stackOffsetWiggle) | |
const xValue = d => d.time | |
const xScale = d3.scaleLinear() | |
const yScale = d3.scaleLinear() | |
const colorScale = d3.scaleOrdinal().range(d3.schemeCategory10) | |
const area = d3.area() | |
.x(d => xScale(xValue(d.data))) | |
.y0(d => yScale(d[0])) | |
.y1(d => yScale(d[1])) | |
.curve(d3.curveBasis) | |
const render = (data) => { | |
stack.keys(data.keys) | |
colorScale.domain(data.keys) | |
const stacked = stack(data) | |
xScale | |
.domain(d3.extent(data, d => xValue(d))) | |
.range([0, width]) | |
yScale | |
.domain([ | |
d3.min(stacked[0], d => d[0]), | |
d3.max(stacked[stacked.length - 1], d => d[1]) | |
]) | |
.range([height, 0]) | |
const transition = d3.transition().duration(1000) | |
const paths = svg.selectAll('path').data(stacked) | |
paths | |
.enter().append('path') | |
.merge(paths) | |
.attr('fill', d => colorScale(d.key)) | |
.attr('stroke', d => colorScale(d.key)) | |
.transition(transition) | |
.attr('d', area) | |
const labels = svg.selectAll('.area-label').data(stacked) | |
labels | |
.enter().append('text') | |
.attr('class', 'area-label') | |
.merge(labels) | |
.text(d => d.key) | |
.transition(transition) | |
.attr('transform', d3.areaLabel(area)) | |
} | |
const renderGeneratedData = () => { | |
render(generateData([ | |
'Leonardo', | |
'Donatello', | |
'Raphael', | |
'Michelangelo' | |
])) | |
} | |
renderGeneratedData() | |
setInterval(renderGeneratedData, 2000) | |
</script> | |
</body> | |
</html> |