
用 D3.js 构建可交互数据可视化OpenMontage 的 d3-viz 技能实战指南【免费下载链接】OpenMontageWorlds first open-source, agentic video production system. 12 production pipelines, 100 tools, 700 agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage本文围绕 OpenMontage 仓库中 d3-viz 技能文档 展开系统讲解如何在任何 JavaScript 环境中原生 JS、React、Vue、Svelte用 D3.js 打造自定义、可交互、出版级的 SVG 数据可视化。你会掌握技能推荐的两种集成模式、完整的图表绘制流程、响应式适配方案以及柱状图、折线图、弦图、热力图、饼图、力导向网络图等高频图表的可直接复用代码并了解该技能在 OpenMontage 视频生产管线中的数据可视化场景配合 数据可视化策略技能如何落地。技能定位什么时候用 D3.jsD3.jsData-Driven Documents数据驱动文档的核心思想是把数据绑定到 DOM 元素上再通过数据驱动的变换scales、layouts、transitions生成视觉元素。它不像标准图表库那样给出开箱即用的成品图表而是提供一整套「乐高积木」让你对每个视觉元素拥有细粒度控制权。根据技能文档的说明在以下场景优先使用 D3.js需要独特视觉编码或自定义布局的定制化可视化需要复杂平移pan、缩放zoom、刷选brush的交互式探索网络 / 图谱类可视化力导向布局、树形图、层级图、弦图使用自定义投影的地理可视化需要平滑、编舞式choreographed过渡动画的可视化追求出版级质量、需要细粒度样式控制的作品标准图表库没有的新颖图表类型。技能文档同时明确指出何时应该另选工具3D 可视化请改用 Three.js 而非 D3.js。在 OpenMontage 仓库中也能看到同样的工具划分——Three.js 世界生成、Three.js 资产目录 等工具承担 3D 场景任务而 D3 定位在二维数据图表。从仓库的能力注册表可以确认 D3 技能的实际接线位置diagram_gen 工具 声明其 provider 为mermaid并显式挂载agent_skills [beautiful-mermaid, d3-viz]d3-viz正是作为图表生成的 Layer 3 技能被引用。在 技能索引 中「Data Visualization」创意技能也将d3-viz与remotion-best-practices列为它依赖的底层技能用于「图表类型选择、动画、标签放置」。核心工作流技能文档把 D3 使用拆解为四个标准步骤环境搭建 → 选择集成模式 → 结构化绘图代码 → 响应式适配。1. 环境搭建在脚本顶部导入 D3import * as d3 from d3;或使用 CDN 版本7.xscript srchttps://d3js.org/d3.v7.min.js/script所有模块scales、axes、shapes、transitions 等都通过d3命名空间访问无需分别引入。2. 选择集成模式技能文档给出两种模式并明确推荐策略模式 A直接 DOM 操作大多数场景推荐——用 D3 选择 DOM 元素并进行命令式操作适用于任何 JavaScript 环境function drawChart(data) { if (!data || data.length 0) return; const svg d3.select(#chart); // 按 ID、class 或 DOM 元素选择 // 清空上一次渲染的内容 svg.selectAll(*).remove(); // 设定尺寸 const width 800; const height 400; const margin { top: 20, right: 30, bottom: 40, left: 50 }; // 创建 scales、axes 并绘制可视化 // ... d3 code here ... } // 数据变化时调用 drawChart(myData);模式 B声明式渲染适用于带模板的框架——用 D3 做数据计算scales、layouts由框架负责渲染元素function getChartElements(data) { const xScale d3.scaleLinear() .domain([0, d3.max(data, d d.value)]) .range([0, 400]); return data.map((d, i) ({ x: 50, y: i * 30, width: xScale(d.value), height: 25 })); } // React 中: {getChartElements(data).map((d, i) rect key{i} {...d} fillsteelblue /)} // Vue 中: 用 v-for 指令遍历返回的数组 // 原生 JS: 根据返回数据手动创建元素选择标准需要 transitions、interactions 或充分发挥 D3 全部能力时用模式 A简单可视化或框架偏好声明式渲染时用模式 B。3. 结构化绘图代码技能文档推荐在每个绘图函数中遵循统一结构数据校验 → 清空画布 → 定义尺寸与 margin → 创建带 margin 的主分组 → 创建 scales → 创建并挂载 axes → 绑定数据绘制视觉元素function drawVisualization(data) { if (!data || data.length 0) return; const svg d3.select(#chart); // 或传入选择器/元素 svg.selectAll(*).remove(); // 清空上一次渲染 // 1. 定义尺寸 const width 800; const height 400; const margin { top: 20, right: 30, bottom: 40, left: 50 }; const innerWidth width - margin.left - margin.right; const innerHeight height - margin.top - margin.bottom; // 2. 创建带 margin 的主分组 const g svg.append(g) .attr(transform, translate(${margin.left},${margin.top})); // 3. 创建 scales const xScale d3.scaleLinear() .domain([0, d3.max(data, d d.x)]) .range([0, innerWidth]); const yScale d3.scaleLinear() .domain([0, d3.max(data, d d.y)]) .range([innerHeight, 0]); // 注意: SVG 坐标系 Y 轴向下需反转 // 4. 创建并挂载 axes const xAxis d3.axisBottom(xScale); const yAxis d3.axisLeft(yScale); g.append(g) .attr(transform, translate(0,${innerHeight})) .call(xAxis); g.append(g) .call(yAxis); // 5. 绑定数据并创建视觉元素 g.selectAll(circle) .data(data) .join(circle) .attr(cx, d xScale(d.x)) .attr(cy, d yScale(d.y)) .attr(r, 5) .attr(fill, steelblue); } // 数据变化时调用 drawVisualization(myData);几个值得注意的细节svg.selectAll(*).remove()保证重复调用不产生叠加残留Y 轴 range 使用[innerHeight, 0]是因为 SVG 的坐标原点在左上角.join()统一了 enter/update/exit 三态逻辑是 D3 v7 的推荐写法后面性能优化一节还会展开。4. 响应式尺寸适配技能文档提供了两种方案。第一种基于windowresize 事件function setupResponsiveChart(containerId, data) { const container document.getElementById(containerId); const svg d3.select(#${containerId}).append(svg); function updateChart() { const { width, height } container.getBoundingClientRect(); svg.attr(width, width).attr(height, height); // 用新尺寸重绘 drawChart(data, svg, width, height); } // 初始加载时更新 updateChart(); // 窗口尺寸变化时更新 window.addEventListener(resize, updateChart); // 返回清理函数 return () window.removeEventListener(resize, updateChart); } // 用法: // const cleanup setupResponsiveChart(chart-container, myData); // cleanup(); // 组件卸载或元素移除时调用第二种更直接使用ResizeObserver监听容器function setupResponsiveChartWithObserver(svgElement, data) { const observer new ResizeObserver(() { const { width, height } svgElement.getBoundingClientRect(); d3.select(svgElement) .attr(width, width) .attr(height, height); // 重绘 drawChart(data, d3.select(svgElement), width, height); }); observer.observe(svgElement.parentElement); return () observer.disconnect(); }技能文档特别提示无论用哪种方案都要保证 SVG 具备width/height属性或viewBox否则无法正确缩放。常见图表模式柱状图柱状图使用scaleBand定位分类轴scaleLinear映射数值轴function drawBarChart(data, svgElement) { if (!data || data.length 0) return; const svg d3.select(svgElement); svg.selectAll(*).remove(); const width 800; const height 400; const margin { top: 20, right: 30, bottom: 40, left: 50 }; const innerWidth width - margin.left - margin.right; const innerHeight height - margin.top - margin.bottom; const g svg.append(g) .attr(transform, translate(${margin.left},${margin.top})); const xScale d3.scaleBand() .domain(data.map(d d.category)) .range([0, innerWidth]) .padding(0.1); const yScale d3.scaleLinear() .domain([0, d3.max(data, d d.value)]) .range([innerHeight, 0]); g.append(g) .attr(transform, translate(0,${innerHeight})) .call(d3.axisBottom(xScale)); g.append(g) .call(d3.axisLeft(yScale)); g.selectAll(rect) .data(data) .join(rect) .attr(x, d xScale(d.category)) .attr(y, d yScale(d.value)) .attr(width, xScale.bandwidth()) .attr(height, d innerHeight - yScale(d.value)) .attr(fill, steelblue); } // 用法: // drawBarChart(myData, document.getElementById(chart));关键点矩形高度必须用innerHeight - yScale(d.value)计算因为 SVG 的 Y 轴向下增长xScale.bandwidth()给出每个柱的实际宽度。折线图折线图通过d3.line()生成路径const line d3.line() .x(d xScale(d.date)) .y(d yScale(d.value)) .curve(d3.curveMonotoneX); // 平滑曲线 g.append(path) .datum(data) .attr(fill, none) .attr(stroke, steelblue) .attr(stroke-width, 2) .attr(d, line);d3.curveMonotoneX在相邻点之间保持单调性既平滑又不会产生过冲适合时间序列。在 d3-patterns 参考文档 中还有带渐变面积填充的面积图模式d3.area()linearGradient。散点图散点图支持可选的尺寸与颜色编码g.selectAll(circle) .data(data) .join(circle) .attr(cx, d xScale(d.x)) .attr(cy, d yScale(d.y)) .attr(r, d sizeScale(d.size)) // 可选: 尺寸编码 .attr(fill, d colourScale(d.category)) // 可选: 颜色编码 .attr(opacity, 0.7);注意 数据可视化策略技能 中特别提醒在视频场景下避免使用散点图太密集相关性展示优先用双序列折线图。弦图Chord diagram弦图在圆形布局中展示实体间关系用 ribbons丝带表示流向。技能文档给出了完整实现function drawChordDiagram(data) { // data 格式: 含 source、target、value 的对象数组 // 示例: [{ source: A, target: B, value: 10 }, ...] if (!data || data.length 0) return; const svg d3.select(#chart); svg.selectAll(*).remove(); const width 600; const height 600; const innerRadius Math.min(width, height) * 0.3; const outerRadius innerRadius 30; // 由数据构造矩阵 const nodes Array.from(new Set(data.flatMap(d [d.source, d.target]))); const matrix Array.from({ length: nodes.length }, () Array(nodes.length).fill(0)); data.forEach(d { const i nodes.indexOf(d.source); const j nodes.indexOf(d.target); matrix[i][j] d.value; matrix[j][i] d.value; }); // 创建 chord 布局 const chord d3.chord() .padAngle(0.05) .sortSubgroups(d3.descending); const arc d3.arc() .innerRadius(innerRadius) .outerRadius(outerRadius); const ribbon d3.ribbon() .source(d d.source) .target(d d.target); const colourScale d3.scaleOrdinal(d3.schemeCategory10) .domain(nodes); const g svg.append(g) .attr(transform, translate(${width / 2},${height / 2})); const chords chord(matrix); // 绘制 ribbons g.append(g) .attr(fill-opacity, 0.67) .selectAll(path) .data(chords) .join(path) .attr(d, ribbon) .attr(fill, d colourScale(nodes[d.source.index])) .attr(stroke, d d3.rgb(colourScale(nodes[d.source.index])).darker()); // 绘制分组弧 const group g.append(g) .selectAll(g) .data(chords.groups) .join(g); group.append(path) .attr(d, arc) .attr(fill, d colourScale(nodes[d.index])) .attr(stroke, d d3.rgb(colourScale(nodes[d.index])).darker()); // 添加标签 group.append(text) .each(d { d.angle (d.startAngle d.endAngle) / 2; }) .attr(dy, 0.31em) .attr(transform, d rotate(${(d.angle * 180 / Math.PI) - 90})translate(${outerRadius 30})${d.angle Math.PI ? rotate(180) : }) .attr(text-anchor, d d.angle Math.PI ? end : null) .text((d, i) nodes[i]) .style(font-size, 12px); }实现要点先由source/target扁平化去重得到节点列表再构造邻接矩阵chord(matrix)返回 ribbons 与chords.groups两组数据分别用d3.ribbon()和d3.arc()绘制标签通过计算弧的中间角度再旋转定位位于下半圆角度大于 π时翻转 180° 避免文字颠倒。热力图热力图用颜色编码二维网格中的数值适合展示跨类别的模式。技能文档的完整实现要点如下行列各用一个d3.scaleBand().padding(0.01)产生细缝效果颜色使用d3.scaleSequential(d3.interpolateYlOrRd)domain 从 0 到最大值图例legend用d3.scaleLineard3.axisRight并通过逐像素绘制 1px 矩形拼接出颜色渐变条for (let i 0; i legendHeight; i) { legend.append(rect) .attr(y, i) .attr(width, legendWidth) .attr(height, 1) .attr(fill, colourScale(legendScale.invert(i))); }行列标签分别放在网格上方与左侧文字锚点text-anchor分别取middle和end。饼图饼图是 D3 中最精简的模式之一核心只有三步d3.pie()计算扇形角度 →d3.arc()生成路径 →scaleOrdinal分配颜色const pie d3.pie() .value(d d.value) .sort(null); // 保持数据原有顺序 const arc d3.arc() .innerRadius(0) // 设为 0 即变成环形图 (donut) .outerRadius(Math.min(width, height) / 2 - 20); const colourScale d3.scaleOrdinal(d3.schemeCategory10); const g svg.append(g) .attr(transform, translate(${width / 2},${height / 2})); g.selectAll(path) .data(pie(data)) .join(path) .attr(d, arc) .attr(fill, (d, i) colourScale(i)) .attr(stroke, white) .attr(stroke-width, 2);.sort(null)会保留数据的原始顺序innerRadius从 0 改为正值即得到环形图中心可放总量标签——这一点与 数据可视化策略技能 中「环形图中心放总量或关键指标」的建议完全吻合。力导向网络图力导向图通过d3.forceSimulation模拟物理布局是交互式网络可视化的代表const simulation d3.forceSimulation(nodes) .force(link, d3.forceLink(links).id(d d.id).distance(100)) .force(charge, d3.forceManyBody().strength(-300)) .force(center, d3.forceCenter(width / 2, height / 2)); const link g.selectAll(line) .data(links) .join(line) .attr(stroke, #999) .attr(stroke-width, 1); const node g.selectAll(circle) .data(nodes) .join(circle) .attr(r, 8) .attr(fill, steelblue) .call(d3.drag() .on(start, dragstarted) .on(drag, dragged) .on(end, dragended)); simulation.on(tick, () { link .attr(x1, d d.source.x) .attr(y1, d d.source.y) .attr(x2, d d.target.x) .attr(y2, d d.target.y); node .attr(cx, d d.x) .attr(cy, d d.y); }); function dragstarted(event) { if (!event.active) simulation.alphaTarget(0.3).restart(); event.subject.fx event.subject.x; event.subject.fy event.subject.y; } function dragged(event) { event.subject.fx event.x; event.subject.fy event.y; } function dragended(event) { if (!event.active) simulation.alphaTarget(0); event.subject.fx null; event.subject.fy null; }物理模拟在tick事件中持续更新坐标拖拽时通过设置fx/fy固定节点松开后置空使其回归物理体系alphaTarget(0.3)让模拟在被拖拽时重新激活。仓库的 sample-data.json 示例数据 中就包含一组 8 节点 9 边的network数据可直接用于测试该模式。交互tooltip、缩放与点击Tooltip技能文档推荐在 SVG 外部创建绝对定位的 div 作为 tooltip避免被 SVG 裁剪并通过 mouseover/mousemove/mouseout 三组事件驱动显示与定位// 在 SVG 外部创建 tooltip div const tooltip d3.select(body).append(div) .attr(class, tooltip) .style(position, absolute) .style(visibility, hidden) .style(background-color, white) .style(border, 1px solid #ddd) .style(padding, 10px) .style(border-radius, 4px) .style(pointer-events, none); // 绑定到元素上 circles .on(mouseover, function(event, d) { d3.select(this).attr(opacity, 1); tooltip .style(visibility, visible) .html(strong${d.label}/strongbr/Value: ${d.value}); }) .on(mousemove, function(event) { tooltip .style(top, (event.pageY - 10) px) .style(left, (event.pageX 10) px); }) .on(mouseout, function() { d3.select(this).attr(opacity, 0.7); tooltip.style(visibility, hidden); });.style(pointer-events, none)防止 tooltip 自身拦截鼠标事件造成闪烁用function而非箭头函数是因为需要在事件处理器内通过this访问当前元素。缩放与平移d3.zoom是标准方案核心是监听 zoom 事件并把event.transform应用到绘图分组const zoom d3.zoom() .scaleExtent([0.5, 10]) .on(zoom, (event) { g.attr(transform, event.transform); }); svg.call(zoom);scaleExtent限制缩放倍率范围0.5x10x。在 interactive-template 模板 中zoom 变换与 margin 偏移做了组合g.attr(transform,translate(${margin.left event.transform.x},${margin.top event.transform.y}) scale(${event.transform.k}))。点击交互circles .on(click, function(event, d) { // 处理点击 (派发事件、更新应用状态等) console.log(Clicked:, d); // 视觉反馈 d3.selectAll(circle).attr(fill, steelblue); d3.select(this).attr(fill, orange); // 可选: 派发自定义事件供框架/应用监听 // window.dispatchEvent(new CustomEvent(chartClick, { detail: d })); });过渡与动画技能文档将动画视为 D3 可视化的核心体验给出了四类基础过渡写法// 基础过渡 circles .transition() .duration(750) .attr(r, 10); // 链式过渡 (先变色再变大) circles .transition() .duration(500) .attr(fill, orange) .transition() .duration(500) .attr(r, 15); // 交错过渡 (逐个延迟形成入场波浪) circles .transition() .delay((d, i) i * 50) .duration(500) .attr(cy, d yScale(d.value)); // 自定义缓动 circles .transition() .duration(1000) .ease(d3.easeBounceOut) .attr(r, 10);在 d3-patterns 参考文档 中进一步给出了enter/update/exit 三态过渡的完整写法exit 元素先过渡到r0再.remove()enter 元素从r0过渡到目标半径关键点是.data(data, d d.id)中的 key 函数它保证对象恒定性object constancy让 D3 能正确区分新增、更新与删除的元素。还有路径变形path morphing模式通过d3.interpolatePath在两个数据集间平滑过渡折线路径。Scales 参考比例尺scales是 D3 数据映射的基石。scale-reference 参考文档 将其分为三大类。连续型比例尺Continuous scales比例尺特点典型用途scaleLinear线性插值最常用坐标轴、柱长、温度/价格/计数scalePow/scaleSqrt幂变换感知缩放面积编码圆半径、突出小值差异scaleLog对数变换domain 必须为正跨数量级数据人口、GDPscaleTime时间专用线性比例尺时间序列、时间轴scaleQuantize连续输入 → 离散桶数据分箱、热力图颜色、风险分级scaleQuantile按分位数划分等量组百分位分类、偏态分布scaleThreshold自定义阈值切分成绩等级、空气质量指数常用方法包括.nice()将 domain 扩展到整数圆整值、.clamp(true)限制输出不越界、.invert()由输出反推输入、.ticks(n)/.tickFormat(n, .2f)生成刻度值与格式化函数。时间比例尺还支持ticks(d3.timeYear)、ticks(d3.timeMonth, 3)等时间粒度刻度。序数型比例尺Ordinal scales比例尺特点典型用途scaleBand离散输入 → 连续条带柱状图、分组元素、热力图单元格scalePoint离散输入 → 连续点无宽度折线图分类轴、网络图节点定位scaleOrdinal离散输入 → 离散输出分类颜色、图例、多序列scaleBand的 padding 参数可拆分为paddingInner条带间与paddingOuter边缘align控制对齐方式scaleOrdinal配合d3.schemeCategory10等内置色板使用也可传入自定义颜色数组。连续颜色比例尺Sequential scalesscaleSequential连续色阶如d3.interpolateBlues、d3.interpolateViridisscaleDiverging以中点为中心的发散色阶如d3.interpolateRdBudomain 用三段式[-10, 0, 10]scaleSequentialQuantile分位数 连续色阶的组合适合含离群值的偏态数据。实用组合模式来自参考文档// 半径比例尺感知正确: 面积随值线性增长 const radiusScale d3.scaleSqrt() .domain([0, d3.max(data, d d.value)]) .range([0, 50]); // 多停靠点渐变 const multiStop d3.scaleLinear() .domain([0, 25, 50, 75, 100]) .range([#d53e4f, #fc8d59, #fee08b, #e6f598, #66c2a5]); // 自适应比例尺: 跨度超 2 个数量级自动切换 log function createAdaptiveScale(data) { const extent d3.extent(data); if (extent[1] / extent[0] 100) { return d3.scaleLog().domain(extent).range([0, width]); } return d3.scaleLinear().domain(extent).range([0, width]); }最佳实践数据准备可视化前务必校验和清洗数据// 过滤非法值 const cleanData data.filter(d d.value ! null !isNaN(d.value)); // 顺序敏感时先排序 const sortedData [...data].sort((a, b) b.value - a.value); // 解析日期 const parsedData data.map(d ({ ...d, date: d3.timeParse(%Y-%m-%d)(d.date) }));性能优化技能文档明确给出大数据集超过 1000 个元素时的策略用 canvas 替代 SVG 渲染海量元素用 quadtree 做碰撞检测用d3.line().curve(d3.curveStep)简化路径大型列表实现虚拟滚动自定义动画使用requestAnimationFrame。此外在 d3-patterns 参考文档 的 enter/update/exit 模式中还强调优先使用.join()而不是分开写 enter/update/exit 三组选择可以避免不必要的重渲染。可访问性// 添加 ARIA 标签 svg.attr(role, img) .attr(aria-label, Bar chart showing quarterly revenue); // 添加标题与描述 svg.append(title).text(Quarterly Revenue 2024); svg.append(desc).text(Bar chart showing revenue growth across four quarters);此外还应保证充足的颜色对比度、为交互元素提供键盘导航、提供数据表格作为替代呈现。样式规范技能文档建议在代码中预先定义统一调色板与排版规范// 预先定义调色板 const colours { primary: #4A90E2, secondary: #7B68EE, background: #F5F7FA, text: #333333, gridLines: #E0E0E0 }; // 统一字体排版 svg.selectAll(text) .style(font-family, Inter, sans-serif) .style(font-size, 12px); // 使用细网格线 g.selectAll(.tick line) .attr(stroke, colours.gridLines) .attr(stroke-dasharray, 2,2);颜色方案选型colour-schemes 参考文档 提供了完整的 D3 颜色方案图谱。内置分类色板d3.schemeCategory1010 色默认选择色盲友好性较好适合通用分类数据d3.schemeTableau1010 色为数据可视化优化专业外观适合商业仪表盘d3.schemePaired12 色成对同色系深浅变体适合分组柱状图、前后对比d3.schemePastel1/2、d3.schemeSet1/2/3、d3.schemeAccent、d3.schemeDark2按视觉层次需求选用。顺序色阶单色系interpolateBlues/Greens/Oranges/Purples/Reds/Greys感知均匀的多色系interpolateViridis推荐、Plasma、Inferno、Magma、Cividis色盲优化、Turbo传统多色系interpolateYlOrRd、interpolateYlGnBu等。发散色阶interpolateRdBu温度隐喻、interpolateRdYlBu、interpolateRdYlGn红-黄-绿信号灯、interpolateSpectral、interpolateBrBG等适用于有语义中点零值、平均值的数据。色盲安全选型参考文档给出明确的快速决策表例如分类数据用 Okabe-Ito 调色板顺序数据用interpolateViridis/interpolateCividis/interpolateBlues发散数据用蓝橙系interpolateBrBG或interpolatePuOr替代红绿组合红绿是色盲人群最常见的识别障碍。测试工具包括 Chrome DevTools 的视觉缺陷模拟、Color Oracle 桌面应用等。动态颜色选择根据数据特征自动选色function selectColourScheme(data) { const extent d3.extent(data); const hasNegative extent[0] 0; const hasPositive extent[1] 0; if (hasNegative hasPositive) { // 数据跨零: 用发散色阶 return d3.scaleSequentialSymlog(d3.interpolateRdBu) .domain([extent[0], 0, extent[1]]); } else { // 全正或全负: 用顺序色阶 return d3.scaleSequential(d3.interpolateViridis) .domain(extent); } }应避免的颜色错误彩虹渐变用于顺序数据不感知均匀、难读改用 Viridis红绿做发散对比色盲不可区分改用蓝橙单图超过 5-8 个分类色难分辨难记忆对比度不足WCAG 要求正文 4.5:1、大字 3:1温度色标颠倒红热、蓝冷。与 OpenMontage 视频生产管线的结合在 OpenMontage 中d3-viz并非孤立存在而是嵌入在视频生产管线的数据可视化场景中。从 diagram_gen 工具 的agent_skills [beautiful-mermaid, d3-viz]可以看到Agent 在需要生成图表时会按需加载本技能数据可视化策略技能 则定义了「为视频而设计」的上层约束两者形成「策略层 技术层」的配合。策略层中与本技能强相关的约束包括图表类型决策树策略文档3-9 个数据点才用图表比较类数据用柱状图、趋势类用折线图、整体构成用饼/环形图最多 5-6 片、KPI 用统计卡网格、排序用降序横向柱状图。少于 3 个数据点时应使用文本或统计卡而非图表——这与 D3 技能文档强调的「先判断是否值得可视化」一脉相承。动画时序策略文档每张视频图表都必须动画化采用 build-up从空框架开始依次生长、narrative highlight随旁白逐个高亮、comparison reveal基线对比后动画变化三种模式之一图表构建 2-4 秒 停留 3-5 秒是硬性时长规则。D3 技能中的交错过渡delay((d, i) i * 50)与链式过渡正是实现 build-up 动画的底层手段。标签放置与字号下限策略文档1080p 输出下标题最小 32px、轴标签与数值标签最小 24px、注释 20px、来源引用 16px4K 输出全部翻倍柱状图数值标签放在柱顶或柱内、饼图小扇区用引线外标等规则均需要在 D3 的.attr(font-size, ...)与text-anchor设置中落实。颜色必须从 playbook 派生策略文档图表颜色优先取playbook.visual_language.color_palette.chart_palette否则从 primary/accent 交替派生。仓库实际配置印证了这一点——clean-professional.yaml 明确定义了 6 色chart_palette#2563EB、#F59E0B、#10B981、#8B5CF6、#EF4444、#06B6D4并开启colorblind_safe: true与contrast_validation: true。而 Remotion 侧的 BarChart 组件 默认色板正是[#2563EB, #F59E0B, #10B981, #EC4899, #06B6D4, #8B5CF6]与 playbook 的 chart_palette 高度一致。对比度与无障碍有自动化保障主题文本对比度契约测试 以 WCAG 2.1 AA 的 4.5:1 为最低对比度MIN_CONTRAST 4.5逐字面解析 Remotion 组件中的颜色值模拟 GPU 合成方式_composite函数将带透明度的前景色与背景色混合再断言实际交付到画面上的对比度达标——这是「颜色不能只靠 D3 图表美观还要在视频合成链路中可读」的工程化保障。常见问题与解决方案技能文档整理的高频问题速查坐标轴不显示确保 scales 的 domain 有效检查 NaN 值确认 axis 挂载到了正确的分组检查 transform 平移是否正确。过渡动画不工作必须在属性变化前调用.transition()为元素提供唯一 key 以保证正确的数据绑定见 enter/update/exit 的 key 函数在 React 中检查 useEffect 依赖是否包含所有变化的数据。响应式尺寸失效使用 ResizeObserver 或 window resize 监听将尺寸更新到状态中以触发重渲染确保 SVG 有width/height属性或viewBox。性能问题限制 DOM 元素数量超过 1000 个元素考虑 canvas防抖 resize 处理器用.join()替代分散的 enter/update/exit 选择检查依赖、避免不必要重渲染。随技能附带的资源技能目录下包含三类可直接使用的配套资源references/ 参考材料d3-patterns.md可视化模式与代码示例合集覆盖树形图、矩形树图、旭日图、弦图、热力图、渐变面积图、堆叠柱状图、分组柱状图、气泡图、地理可视化d3.geoMercator地图 点标记、d3.geoPath分级统计图、刷选d3.brush、跨图联动刷选、enter/update/exit 过渡、路径变形等进阶模式scale-reference.md完整比例尺指南上文已系统梳理colour-schemes.md颜色方案与调色板建议上文已系统梳理。assets/ 样板模板chart-template.jsxReact 基础图表模板演示了useRefuseEffect D3 的标准组合——d3.select(svgRef.current)、清空画布、带 margin 的分组、scaleBand/scaleLinear双比例尺、坐标轴、.join(rect)绘制柱状图及轴标签interactive-template.jsx带 tooltip、缩放、点击选择与 React 状态联动的交互式散点图模板并示范了入场动画attr(r, 0)后 transition 到目标半径与网格线绘制技巧sample-data.json覆盖时间序列、分类、散点、层级、网络、堆叠、地理点、发散数据八种结构的示例数据集可直接喂给上述模板做测试。技能文档明确说明这些模板可适配原生 JavaScript、React、Vue、Svelte 等任何 JavaScript 环境按需改造即可当需要特定图表类型的详细指导时再读取 references 中的对应文件。【免费下载链接】OpenMontageWorlds first open-source, agentic video production system. 12 production pipelines, 100 tools, 700 agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考