 角度计算、排序、间隙角与弧生成器协作机制)
d3 饼图生成器详解d3.pie() 角度计算、排序、间隙角与弧生成器协作机制【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3本文以 d3 官方文档中的饼图生成器pie generator页面为主体系统讲解d3.pie()的构造、生成结果结构、取值访问器、数据比较器、起止角与 padAngle 间隙角的完整 API 语义并结合本仓库中的示例组件与构建配置说明饼图角度是如何与弧生成器arc generator协作渲染出饼图/环形图的。读完后你将能够独立配置一个生产可用的 d3 饼图/甜甜圈图并理解每个角度参数在底层计算中的作用。饼图生成器的定位只算角度不画形状d3 的形状生成模块d3-shape将数据到角度与角度到路径两件事拆分为两个生成器饼图生成器d3.pie()负责把一组表格型数据换算成表示饼图或环形图所需的各个扇区角度弧生成器d3.arc()负责把角度与半径换算成 SVG path 数据字符串。官方文档对此有一句关键限定见 docs/d3-shape/pie.md饼图生成器不直接产出形状它的输出是可传入弧生成器的角度描述对象。这种数据 → 角度 → 路径的两段式管线是 d3 形状模块的核心设计思想。从源码结构看本仓库d3 7.9.0 主包通过 src/index.js 第 25 行的export * from d3-shape将 d3-shape 的全部 API 重导出package.json 中声明了依赖d3-shape: ^3.2.0因此d3.pie()与d3.arc()均来自 d3-shape 子包并在d3命名空间下直接可用。bundle.js 只是对src/index.js的再导出配合rollup -c见 rollup.config.js 与 prebuild.sh产出最终发行包。pie()构造生成器pie()用默认设置构造一个新的饼图生成器const pie d3.pie();文档中标注其实现位于 d3-shape 包的src/pie.js本仓库内可通过依赖d3-shape的源码查看。默认参数组合为值访问器恒等返回数据本身、数据比较器为 null、值比较器为降序、起始角 0、结束角 2π、padAngle 为 0。pie(data, ...arguments)生成角度数组调用生成器时传入数据数组返回一个每个数据项对应一个角度对象的数组。文档给出的示例是斐波那契数列const data [1, 1, 2, 3, 5, 8, 13, 21]; const pie d3.pie(); const arcs pie(data);得到的arcs是一个对象数组以下为文档中的原始输出[ {data: 1, value: 1, index: 6, startAngle: 6.050474740247008, endAngle: 6.166830023713296, padAngle: 0}, {data: 1, value: 1, index: 7, startAngle: 6.166830023713296, endAngle: 6.283185307179584, padAngle: 0}, {data: 2, value: 2, index: 5, startAngle: 5.817764173314431, endAngle: 6.050474740247008, padAngle: 0}, {data: 3, value: 3, index: 4, startAngle: 5.468698322915565, endAngle: 5.817764173314431, padAngle: 0}, {data: 5, value: 5, index: 3, startAngle: 4.886921905584122, endAngle: 5.468698322915565, padAngle: 0}, {data: 8, value: 8, index: 2, startAngle: 3.956079637853813, endAngle: 4.886921905584122, padAngle: 0}, {data: 13, value: 13, index: 1, startAngle: 2.443460952792061, endAngle: 3.956079637853813, padAngle: 0}, {data: 21, value: 21, index: 0, startAngle: 0.000000000000000, endAngle: 2.443460952792061, padAngle: 0} ]每个对象包含以下六个属性data—— 输入数据项即输入数组中对应的元素value—— 该扇区的数值由值访问器计算见下文index—— 该扇区经过排序后的零基下标startAngle—— 扇区起始角endAngle—— 扇区结束角padAngle—— 扇区间隙角本例为 0。这个对象结构与弧生成器的默认startAngle、endAngle、padAngle访问器完全对齐可参见 docs/d3-shape/arc.md 中对应访问器默认读取d.startAngle等字段的定义。角度以弧度表示0 对应 -y 方向12 点钟方向正角度顺时针推进。两条重要的顺序性保证值得牢记返回数组长度与data相同且返回数组中第i个元素对应输入数组第i个元素——即使开启了排序输出顺序仍与输入一致调用时附带的额外arguments是任意参数会与this上下文一起透传给生成器的各访问器函数因此数据项可直接作为上下文传入。这一点在仓库自带的演示组件 docs/components/ExampleArcs.vue 中得到了印证它用pie(data)的结果数组直接对path元素做数据绑定.data(pie(data)).join(path).attr(d, ...)正是利用输出顺序 输入顺序这一性质来稳定地更新 DOM。pie.value(value)值访问器若指定value则设置值访问器并返回该生成器const pie d3.pie().value((d) d.value);生成时值访问器会对输入数组的每个元素调用一次依次接收d元素、i下标、data数组三个参数。若不指定value则返回当前值访问器pie.value() // (d) d.value值访问器默认为恒等函数function value(d) { return d; }这意味着默认情况下输入数据要么是数字要么能通过valueOf强制转换为数字。如果数据不是数字就应提供一个返回数值的访问器。文档给出的典型场景是加载 CSV含number与name字段number,name 4,Locke 8,Reyes 15,Ford 16,Jarrah 23,Shephard 42,Kwonconst data await d3.csv(lost.csv, d3.autoType); const pie d3.pie().value((d) d.number); const arcs pie(data);这与在调用饼图生成器前先把数据映射成数值等价const arcs d3.pie()(data.map((d) d.number));两者的差异在于访问器保留了原始数据与返回对象的关联每个输出对象的data字段仍是原始记录后续给扇区上色、加文本标签时可以直接读取记录的其他字段如按d.name取颜色无需再回查原数组。这是实际项目里推荐使用访问器而非先map的原因。pie.sort(compare)数据比较器若指定compare则设置数据比较器并返回该生成器const pie d3.pie().sort((a, b) d3.ascending(a.name, b.name));数据比较器接收输入数组中的两个元素a和b若a的扇区应排在b之前返回小于零的数若应排在之后返回大于零的数返回零表示a与b的相对顺序未指定。不指定compare时返回当前比较器pie.sort() // (a, b) d3.ascending(a.name, b.name))默认的数据比较器为null。当数据比较器与值比较器都为 null 时扇区按原始输入顺序排布设置数据比较器会隐式地把值比较器置为 null两者互斥。文档特别强调排序不会改变生成的扇区数组的顺序——该数组始终与输入数据数组同序排序只影响每个扇区计算出的角度。第一个扇区从起始角开始最后一个扇区到结束角结束。结合上文斐波那契示例可以看出输入[1, 1, 2, 3, 5, 8, 13, 21]中最大的 21 恰好排到startAngle: 0的位置、index: 0而输出数组里 21 仍在末尾与输入位置一致——角度按降序分配、数组顺序不变的机制在示例数据上自洽。pie.sortValues(compare)值比较器若指定compare则设置值比较器并返回该生成器const pie d3.pie().sortValues(d3.ascending);值比较器与数据比较器的语义类似区别在于传入的a和b不是数据元素本身而是经值访问器求得的数值。返回值的含义小于零在前、大于零在后、零未指定与数据比较器相同。不指定compare时返回当前值比较器pie.sortValues() // d3.ascending值比较器默认为降序descending参见 docs/d3-array/sort.md 中descending的定义即默认饼图按数值从大到小顺时针排列。数据比较器与值比较器同为 null 时扇区按原始顺序设置值比较器会隐式地把数据比较器置为 null。与数据比较器一样排序只影响角度计算不影响输出数组顺序第一个扇区从起始角开始、最后一个扇区到结束角结束。pie.startAngle(angle) 与pie.endAngle(angle)整体起止角这两个方法设置的是整个饼图的起止角即第一个扇区的起始角与最后一个扇区的结束角const pie d3.pie().startAngle(0); const pie d3.pie().endAngle(Math.PI);通常传入常量数字也可以传入数据的函数作为函数时该访问器只被调用一次接收的参数与this上下文同饼图生成器本身。不指定angle时分别返回当前访问器pie.startAngle() // () 0 pie.endAngle() // () Math.PI默认访问器分别为function startAngle() { return 0; } function endAngle() { return 2 * Math.PI; }角度同样是弧度制0 在 12 点钟方向、顺时针为正。一个约束值得注意结束角的取值被限制在 startAngle ± τ 范围内即 |endAngle - startAngle| ≤ τ2π。这个约束对半环图环形进度条等场景很关键——例如endAngle(Math.PI)恰好得到上半圆0 到 π 覆盖 12 点钟顺时针到 6 点钟方向而超过 τ 的跨度会被截断。pie.padAngle(angle)扇区间隙角若指定angle则设置间隙角并返回该生成器const pie d3.pie().padAngle(0.03);间隙角指定相邻扇区之间的角度间隔弧度。总的间隙量 指定 angle × 输入数组元素个数且最多不超过 |endAngle - startAngle|扣掉间隙后的剩余角度按 value成比例分配从而保证各扇区的相对面积不变——这就是加间隙不扭曲占比的底层机制。间隙角通常是常量也可以写成数据的函数作为函数时同样只调用一次参数与this同生成器。不指定时返回当前访问器pie.padAngle() // () 0默认访问器为() 0即默认无间隙。文档页面为 padAngle 配了一个可交互滑块0 到 0.1步长 0.001默认 0.03动态演示扇区间隔的变化。该演示由本仓库的 docs/components/ExampleArcs.vue 组件驱动其渲染逻辑值得细读它完整展示了 pie 与 arc 的协作方式const data [1, 1, 2, 3, 5, 8, 13, 21]; const pie d3.pie().padAngle(padAngle); const arc d3.arc().innerRadius(innerRadius).outerRadius(outerRadius).padRadius(padRadius); svg.selectChildren([fillcurrentColor]) .selectAll(path) .data(pie(data)) .join(path) .attr(d, arc.cornerRadius(cornerRadius));注意三个细节pie(data)的输出直接作为arc的输入因为 pie 输出的startAngle/endAngle/padAngle字段名与 arc 的默认访问器一一对应组件把 innerRadius 设为 outerRadius 的 1/3环形图这正是让 padAngle 视觉效果正确的场景——docs/d3-shape/arc.md 中明确说明间隙通常应施加在环形扇区innerRadius 为正上并给出最小内半径经验式outerRadius * padAngle / sin(θ)θ 为加间隙前最小扇区的角跨度arc 侧的padRadius用于把角度间隙换算成固定的线性距离padRadius × padAngle默认自动取sqrt(innerRadius² outerRadius²)这也是 pie 生成的 padAngle 能在不同半径下保持视觉均匀的原因。完整工作流从数据到 SVG 路径把上述 API 串起来一个标准的饼图/甜甜圈图数据流如下// 1. 计算角度d3-shape 的 pie 生成器 const pie d3.pie() .value((d) d.number) // 非数值数据必须提供访问器 .sortValues(d3.descending) // 默认即为降序也可改用 sort 按字段排序 .startAngle(0) .endAngle(2 * Math.PI) .padAngle(0.03); const arcs pie(data); // 顺序 输入顺序含 data/value/index/startAngle/endAngle/padAngle // 2. 生成路径d3-shape 的 arc 生成器 const arc d3.arc() .innerRadius(outerRadius / 3) // 环形图间隙角建议配合内半径使用 .outerRadius(outerRadius); // 3. 数据绑定并渲染d3-selection svg.selectAll(path) .data(arcs) .join(path) .attr(d, arc) .attr(fill, d color(d.data.name)); // d.data 仍是原始记录其中第 3 步的d.data.name正是值访问器保留原始数据关联收益的体现。仓库文档 docs/d3-shape/arc.md 还给出了把生成的弧平移到目标位置的惯用法arc 以原点为中心用 transform 移动svg.append(path) .attr(transform, translate(100,100)) .attr(d, arc);本仓库中的相关工程事实API 来源与版本package.json 声明 d3 主包版本 7.9.0、依赖d3-shape ^3.2.0src/index.js 通过export * from d3-shape把pie、arc等并入d3命名空间因此在 ESM 与 UMDdist/d3.min.js见 package.json 的jsdelivr/unpkg字段两种消费方式下行为一致。文档链接质量保障test/docs-test.js 会爬取docs/下全部 Markdown 的内部链接与锚点并断言无断链docs:build流程依赖./prebuild.sh与 mocha 测试见 package.json 的scripts。这意味着 docs/d3-shape/pie.md 中指向./arc.md等相对锚点的引用在仓库 CI 里是被机器校验过的。可运行的参考实现docs/components/ExampleArcs.vue 展示了 padAngle 与 cornerRadius 两个维度的组合渲染是理解pie 出角度、arc 出路径分工的最佳仓库内示例。小结与常见误区d3.pie()只产出角度对象数组不画形状必须搭配d3.arc()才能得到可渲染的 path。输出数组顺序永远等于输入顺序排序sort/sortValues只重排角度因此用索引对齐扇区与数据时不要依赖排序后的位置。sort与sortValues互斥设置其中一个会把另一个置为 null二者皆 null 时按原始输入顺序。默认按值降序排布最大扇区从 12 点钟0 弧度顺时针开始角度单位是弧度顺时针为正。padAngle的总间隙按单位间隙 × 扇区数扣除且不超过总跨度剩余部分按 value 等比例分配占比不失真配合 arc 侧的padRadius可得到线性均匀的间隔视觉效果推荐用于 innerRadius 为正的环形扇区。endAngle 被约束在 startAngle ± τ 内制作半环、进度环时应据此显式设置起止角。【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考