如何在 Svelte 中结合 D3 构建响应式折线图?
【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3
在 Svelte 项目里用 D3 画折线图,并且让图表随数据变化自动更新,是 D3 官方文档明确支持的一种集成方式。docs/getting-started.md 的 “D3 in Svelte” 章节给出了两个完整的LinePlot.svelte组件示例:第一个只使用不操作 DOM 的 D3 模块(d3-shape、d3-scale),由 Svelte 全权负责渲染;第二个在此基础上用 Svelte 的$:响应式语句驱动 d3-axis,让坐标轴随数据变化重绘。前提是你已有一个 Svelte 项目,并通过包管理器安装了 D3。
官方文档给出的两条集成路线
D3 文档在 docs/what-is-d3.md 中把模块按是否触碰 DOM 做了区分:selections 类模块(d3-selection、d3-transition 等)直接修改 DOM,而 scales、shapes 类模块“only operate on data”。Svelte 章节沿用了同样的边界(原文以 React 为参照,说明 Svelte 可“exclusively for rendering”,只使用不操作 DOM 的 D3 模块),因此组合方式只有两种:
- 纯声明式:D3 只做比例尺和形状生成,SVG 全部写在 Svelte 模板里,不引入任何 DOM 操作;
- 响应式混合:对 d3-axis 这类必须操作 DOM 的模块,用
bind:this把元素交给 D3,并用$:语句保证数据变化时重新执行。
折线图恰好需要两者:d3-scale + d3-shape 计算折线路径,d3-axis 绘制坐标轴。
准备 D3 依赖
在项目根目录用包管理器安装 D3,docs/getting-started.md 给出了三种方式:
npm install d3yarn add d3pnpm add d3组件内导入:
import * as d3 from "d3";第一步:纯渲染的折线图组件
官方第一个示例是“a line plot of an array of numbers”,只依赖 d3.line 和 d3.scaleLinear。比例尺和折线生成器都写成$:响应式语句,依赖data以及宽高、边距 props:
<!-- LinePlot.svelte --> <script> import * as d3 from 'd3'; export let data; export let width = 640; export let height = 400; export let marginTop = 20; export let marginRight = 20; export let marginBottom = 20; export let marginLeft = 20; $: x = d3.scaleLinear([0, data.length - 1], [marginLeft, width - marginRight]); $: y = d3.scaleLinear(d3.extent(data), [height - marginBottom, marginTop]); $: line = d3.line((d, i) => x(i), y); </script> <svg width={width} height={height}> <path fill="none" stroke="currentColor" stroke-width="1.5" d={line(data)} /> <g fill="white" stroke="currentColor" stroke-width="1.5"> {#each data as d, i} <circle key={i} cx={x(i)} cy={y(d)} r="2.5" /> {/each} </g> </svg>这个组件的关键点:
line(data)返回 path data 字符串,直接赋给d属性,不需要手动创建或修改 SVG 节点;{#each}负责数据点的增删,cx/cy由比例尺计算;- 因为
x、y、line都是$:语句,data或width/height等 prop 变化时会重新计算,路径与数据点随之更新——这就是“响应式”的实现机制,不需要任何手动刷新逻辑。
组件对外只需一个数字数组data(宽高和边距都有默认值),文档描述它即“an array of numbers”的折线图。
第二步:用响应式语句加入动态坐标轴
第一个示例没有坐标轴。官方第二个示例引入 d3-axis:用bind:this拿到两个<g>的 DOM 引用,把d3.select(...).call(...)写进$:语句,数据变化导致比例尺重算时轴也会被重新渲染:
<!-- LinePlot.svelte --> <script> import * as d3 from 'd3'; export let data; export let width = 640; export let height = 400; export let marginTop = 20; export let marginRight = 20; export let marginBottom = 30; export let marginLeft = 40; let gx; let gy; $: x = d3.scaleLinear([0, data.length - 1], [marginLeft, width - marginRight]); $: y = d3.scaleLinear(d3.extent(data), [height - marginBottom, marginTop]); $: line = d3.line((d, i) => x(i), y); $: d3.select(gy).call(d3.axisLeft(y)); $: d3.select(gx).call(d3.axisBottom(x)); </script> <svg width={width} height={height}> <g bind:this={gx} transform="translate(0,{height - marginBottom})" /> <g bind:this={gy} transform="translate({marginLeft},0)" /> <path fill="none" stroke="currentColor" stroke-width="1.5" d={line(data)} /> <g fill="white" stroke="currentColor" stroke-width="1.5"> {#each data as d, i} <circle key={i} cx={x(i)} cy={y(d)} r="2.5" /> {/each} </g> </svg>注意:上面两个<g>的transform属性是 docs/getting-started.md 原文示例的写法,本文保持原样。按 Svelte 语法,双引号属性是普通字符串,其中的{height - marginBottom}、{marginLeft}不会被插值,实际平移不会生效。若要在本地跑起来,需改成单引号属性并对表达式加花括号,例如:
<g bind:this={gx} transform='translate(0,{height - marginBottom})' /> <g bind:this={gy} transform='translate({marginLeft},0)' />文档对第二个示例的定位是:“Svelte’s reactive statements ($:) pair nicely with D3 data joins for efficient updates. Below, we use them to render dynamic axes as the data changes.” 也就是说,坐标轴随数据动态重绘正是这个模式的预期行为。
如何验证结果
官方文档为两个示例各挂了一个 Svelte 在线 REPL / Playground 入口(见 docs/getting-started.md Svelte 章节代码块后的 “REPL ↗︎” 链接),可直接粘贴运行。本地验证时的判断依据:
- 第一个示例:SVG 中出现一条
<path>和与data等量的<circle>,数据点位置与比例尺一致; - 第二个示例:修改
data(或宽高、边距 props)后,坐标轴随之重绘。文档将这一行为表述为 “render dynamic axes as the data changes”,这是第二个示例应当观察到的现象。
边界与下一步
- 纯声明式路线只覆盖折线和数据点。需要坐标轴时必须引入 d3-axis(DOM 操作类模块),改用第二步的
bind:this交接方式; - 文档同时指出,D3 输出的是标准 SVG,可以用外部样式表调整图表外观,包括配合 media queries 做响应式布局(见 docs/what-is-d3.md),因此容器级响应式可交给 CSS 处理;
- 如果数据变化时需要更精细的 enter/update/exit 控制,继续阅读 data joins 章节:
selection.data/selection.join允许你通过 key 函数自行决定数据与元素的匹配策略,从而“minimize changes to the DOM to optimize performance”。
【免费下载链接】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),仅供参考