chart = {
const width = 928;
const height = 500;
const marginTop = 20;
const marginRight = 20;
const marginBottom = 20;
const marginLeft = 40;
const series = d3.stack()
.offset(d3.stackOffsetExpand)
.keys(d3.union(unemployment.map(d => d.industry)))
.value(([, D], key) => D.get(key).unemployed)
(d3.index(unemployment, d => d.date, d => d.industry));
const x = d3.scaleUtc()
.domain(d3.extent(unemployment, d => d.date))
.range([marginLeft, width - marginRight]);
// 设置纵坐标轴的比例尺
const y = d3.scaleLinear()
// 💡 省略设置纵坐标轴比例尺的定义域范围
// 因为标准化后,堆叠面积图的纵轴定义域范围就是 [0, 1] 与线性比例尺的默认定义域相同
.rangeRound([height - marginBottom, marginTop]);
// 设置颜色比例尺
const color = d3.scaleOrdinal()
.domain(series.map(d => d.key))
.range(d3.schemeTableau10);
/**
*
* 创建 svg 容器
*
*/
// 返回的是一个包含 svg 元素的选择集
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto;");
/**
*
* 绘制坐标轴
*
*/
// 绘制横坐标轴
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
// 💡 删掉上一步所生成的坐标轴的轴线(它含有 domain 类名)
// 👇 在后面使用纵坐标轴的刻度线来绘制
.call(g => g.select(".domain").remove());
// 绘制纵坐标轴
svg.append("g")
.attr("transform", `translate(${marginLeft},0)`)
// 并使用坐标轴对象的方法 axis.ticks() 设置坐标轴的刻度数量和刻度值格式
// 其中第一个参数用于设置刻度数量(这里设置的是预期值,并不是最终值,D3 会基于出入的数量进行调整,以便刻度更可视)
// 这里设置为 (height / 80) 基于 svg 的高度来设置纵坐标轴的预期刻度数量
// 而第二个参数用于设置刻度值格式,这里设置为 "%" 表示数值采用百分比表示
.call(d3.axisLeft(y).ticks(height / 80, "%"))
.call(g => g.select(".domain").remove())
// 💡 复制顶部和底部的刻度线,用以绘制图中横向的参考线,作为面积图的上下的边界(但是视觉上其实并不明显,感觉可以省略 ❓)
.call(g => g.selectAll(".tick line")
.filter(d => d === 0 || d === 1) // 筛选出零刻度线和顶部的刻度线
.clone()
// 调整复制后的刻度线的终点位置(往右移动)
.attr("x2", width - marginLeft - marginRight))
.call(g => g.append("text")
.attr("x", -marginLeft)
.attr("y", 10)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("↑ Unemployed persons"));
/**
*
* 绘制面积图内的面积形状
*
*/
// Construct an area shape.
const area = d3.area()
.x(d => x(d.data[0]))
.y0(d => y(d[0]))
.y1(d => y(d[1]));
// Append a path for each series.
svg.append("g")
.selectAll()
.data(series)
.join("path")
.attr("fill", d => color(d.key))
.attr("d", area)
.append("title")
.text(d => d.key);
// Return the chart with the color scale as a property (for the legend).
return Object.assign(svg.node(), {scales: {color}});
}