diff --git a/index.html b/index.html
index 61fb426..d873f97 100644
--- a/index.html
+++ b/index.html
@@ -494,6 +494,12 @@
{{ example.name }}
folder: 'donut-chart',
note: 'https://datavis-note.benbinbin.com/article/d3/chart-example/d3-chart-example-pie-chart#环形图'
},
+ {
+ name: '环形图矩阵',
+ reference: 'https://observablehq.com/@benbinbin/donut-multiples',
+ folder: 'donut-multiples',
+ note: 'https://datavis-note.benbinbin.com/article/d3/chart-example/d3-chart-example-pie-chart#环形图矩阵'
+ },
]
},
{
diff --git a/piechart/donut-multiples/app.js b/piechart/donut-multiples/app.js
new file mode 100644
index 0000000..8655cd2
--- /dev/null
+++ b/piechart/donut-multiples/app.js
@@ -0,0 +1,234 @@
+// 参考自 https://observablehq.com/@d3/donut-multiples
+
+/**
+ *
+ * 构建 svg
+ *
+ */
+const container = document.getElementById("container"); // 图像的容器
+
+/**
+ *
+ * 异步获取数据
+ * 再在回调函数中执行绘制操作
+ *
+ */
+// 数据来源网页 https://observablehq.com/@d3/donut-multiples 的文件附件
+const dataURL =
+ "https://gist.githubusercontent.com/Benbinbin/7f7567ac17e75e5d8248e5dd83eae3d4/raw/6385e6176aa4bd612f6fc3d0906a651da3ece908/state-age.csv";
+
+// 读取 csv 文件
+d3.csv(dataURL, d3.autoType).then((result) => {
+ // 需要检查一下数据解析的结果,可能并不正确,需要在后面的步骤里再进行相应的处理
+ console.log(result);
+
+ /**
+ *
+ * 对原始数据进行转换处理
+ *
+ */
+ // 解析所得的可迭代对象(数组)具有属性 columns
+ // 该属性的值是一个数组,元素分别是原数据的列属性(即原来二维数据表的表头信息)
+ // 这里使用 JS 数组的原生方法 array.slice(1) 从第二个元素开始(包含所有年龄段)将其拷贝一份,用于后面的数据转换
+ const columns = result.columns.slice(1);
+
+ // 遍历每个元素,并基于它生成一个对象 {state: string, sum: number, ages: array} 作为(返回的新数组的对应索引值的)新元素
+ // 该对象各属性的含义:
+ // * 属性 state 是当前所遍历的数据点所属的州的名称,采用当前所遍历元素的属性 d.state
+ // * 属性 sum 是该州的总人数,通过 d3.sum(array, accessor) 进行求和
+ // 其中第一个参数 columns 是一个数组,包含所有年龄段
+ // 而第二个参数是 accessor 数据访问器 (key) => +d[key] 对于每一个年龄段 key,从当前数据点 d 读取出该州相应年龄段的人数(并转换为数值)+d[key]
+ // 最后进行求和就得到该州的总人口数量
+ // * 属性 ages 是一个数组,包含该州具体各个年龄段的人口数量
+ // 基于 columns 数值采用 JS 数组的原生方法 array.map() 进行转换而得
+ // 它的每个元素都是一个对象 {age: string, population: number} 表示年龄段为 key 的人口数量 +d[key]
+ const data = result.map((d, i) => ({
+ state: d.State,
+ sum: d3.sum(columns, (key) => +d[key]),
+ ages: columns.map((key) => ({ age: key, population: +d[key] }))
+ }));
+
+ console.log(data);
+
+ /**
+ *
+ * 构建比例尺
+ *
+ */
+ // 设置颜色比例尺
+ // 为环形图的不同扇形区域设置不同的配色
+ // 使用 d3.scaleOrdinal() 排序比例尺 Ordinal Scales 将离散型的定义域映射到离散型值域
+ // 具体参考官方文档 https://d3js.org/d3-scale/ordinal
+ // 或这一篇笔记 https://datavis-note.benbinbin.com/article/d3/core-concept/d3-concept-scale#排序比例尺-ordinal-scales
+ const color = d3.scaleOrdinal()
+ // 设置定义域范围
+ // 各扇形所表示的年龄段,即 7 种年龄段
+ .domain(columns)
+ // 设置值域范围,对应 7 种颜色值
+ .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
+
+ /**
+ *
+ * 对数据进行转换
+ *
+ */
+ // Create the pie layout and arc generator.
+ // 使用 d3.pie() 创建一个 pie 饼图角度生成器
+ // 饼图角度生成器会基于给定的数据,计算各数据项所对应的扇形在饼图中所占的角度
+ // 调用饼图角度生成器时返回的结果是一个数组,它的长度和入参的数组长度一致,元素的次序也一样,其中每个元素(是一个对象)依次对应一个数据项,并包含以下属性:
+ // * data 数据项的值
+ // * value 一个数值,它代表了该数据项,被用于在 Pie 饼图生成器里进行运算(以计算该数据项所需占据的角度)
+ // * index 数据项的索引,从 0 开始
+ // * starAngle 该数据项在扇形或环形中所对应的起始角
+ // * endAngle 该数据项在扇形或环形中所对应的结束角
+ // * padAngle 扇形或环形的间隔角度
+ // 具体可以参考官方文档 https://d3js.org/d3-shape/pie
+ // 或这一篇笔记 https://datavis-note.benbinbin.com/article/d3/core-concept/d3-concept-shape#pies-饼图角度生成器
+ const pie = d3.pie()
+ // 设置数据项的排序对比函数(用于决定相应扇形的优先次序)
+ // 💡 此处所说的排序,实际上体现在各数据项所对应的扇形的起始角度和结束角度上,而不会改变数组中的元素的次序(经过排序后返回的数组的元素次序,和数据表中数据项的顺序是相同的)
+ // 虽然数据项的排序对比函数默认值就是 `null`,但是这里依然显式地将对比函数设置为 null
+ // 这是为了让 D3 隐式地调用 `pie.sortValues(null)` 将数据项转换值的对比函数设置为 `null`(它的默认值是 `d3.descending` 降序排列),以忽略按数据项的转换值进角度排序
+ // 如果 `pie.sort` 数据项的对比函数 ,以及 `pie.sortValues` 它的转换值的对比函数都是 `null`,则各扇形的排序与原始数据集中各数据项顺序一致
+ // 所以最终各扇形是按照相应的数据项在原始数据集中的顺序进行排序(即年龄段从小到大进行排序)
+ .sort(null)
+ .padAngle(0.02) // 设置环形图里各个扇形之间的间隔角度
+ // 设置 value accessor 数据访问器/读取函数,即每个数据项经过该函数的转换后,再传递给 Pie 饼图角度生成器
+ // 数据读取函数的逻辑要如何写,和后面 👇👇 调用饼图角度生成器时,所传入的数据格式紧密相关
+ // 在后面绘制环状扇形时,向饼图角度生成器所传递的数组是表示某个州的各个年龄段的人口数量
+ // 则在迭代时数据点 d 是一个对象 {age: string, population: number}(表示某个州的特定年龄段的人口数量)
+ // 这里获取获取人口数量 d.population
+ .value((d) => d.population);
+
+ /**
+ *
+ * 绘制环形图
+ * 每个环形图都使用单独的一个