waveform-analysis
v0.1.63
Published
基于 Vue 3、TypeScript 和 D3 的响应式 SVG 波形图组件。适合展示单通道、多通道和大规模采样 数据,内置缩放、tooltip、图例、误差棒、标注、分页和多 Y 轴叠加。
Readme
Waveform Analysis
基于 Vue 3、TypeScript 和 D3 的响应式 SVG 波形图组件。适合展示单通道、多通道和大规模采样 数据,内置缩放、tooltip、图例、误差棒、标注、分页和多 Y 轴叠加。
组件使用不可变数据模型:替换 data 引用后会重新计算数据域和视口;大数据会按当前可见范围
和屏幕像素自动保峰降采样,而 tooltip、最近点查询和标注仍使用完整原始数据。
在线示例
线上 Demo:波形分析组件在线示例。
特性
- Vue 3 Composition API + TypeScript,支持按需导入
WaveformChart - 采样值、显式坐标点和多系列数据模型
independent、separated、compact三种布局模式- 曲线、阶梯线、点符号和对称/非对称误差棒
- 实线、虚线和点划线,可按系列独立配置
- 缩放过程事件、缩放结束按可视区间加载和视口重置
- 可选的空格拖拽平移,默认关闭并隔离多图表实例
- 多系列图例、受控显隐、网格分页和最多四根 Y 轴
- 自动 Y 轴范围,以及全局、按轨道或按系列配置的固定振幅范围
- 按轨道控制水平/垂直网格线的显隐与颜色
- 受控标注、右键编辑、拖拽避让和自定义颜色
- 标题、图框、坐标轴、零值参考线、净图和渲染参数可配置
安装
组件库将 Vue 和 Ant Design Vue 作为 peer dependency;D3 和 vue3-colorpicker 由组件包直接依赖。 安装组件时请确保业务项目同时提供兼容版本的 peer dependency:
pnpm add waveform-analysis vue ant-design-vue运行时版本要求
组件库当前使用或支持以下运行时版本:
| 依赖 | 支持版本 |
| ---------------- | ------------- |
| Vue | >=3.2.33 <4 |
| Ant Design Vue | >=3.2.20 <4 |
| D3 | >=7.9.0 <8 |
| vue3-colorpicker | >=2.3.0 <3 |
其中 Vue 和 Ant Design Vue 的版本范围是公开的 peer dependency 约束;D3 和 vue3-colorpicker 随组件包安装。
最小示例
<script setup lang="ts">
import { ref } from 'vue'
import { WaveformChart, type WaveformData } from 'waveform-analysis'
import 'waveform-analysis/style.css'
const data = ref<WaveformData>({
kind: 'points',
points: [
{ x: 0, y: 0.2 },
{ x: 0.001, y: 0.4 },
{ x: 0.002, y: 0.1 },
],
})
</script>
<template>
<div class="chart-container">
<WaveformChart :data="data" />
</div>
</template>
<style scoped>
.chart-container {
height: 420px;
}
</style>父容器需要有明确高度;未指定 width 或 height 时,组件会填充父容器,并保持最小高度
180px。WaveformChart 的正式入口为 src/index.ts,样式入口为 waveform-analysis/style.css。
API 速查
Props
| Prop | 类型 | 默认值 | 说明 |
| -------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------- |
| data | WaveformData | 必填 | 波形数据 |
| displayMode | 'independent' \| 'separated' \| 'compact' | 'independent' | 图框布局 |
| overlayMode | 'single-axis' \| 'multi-axis' | 'single-axis' | 叠加曲线的 Y 轴模式 |
| timeUnit | 's' \| 'ms' | 'ms' | 坐标轴和 tooltip 展示单位 |
| xLabel / yLabel | string | 时间(timeUnit) / '幅值' | 坐标轴名称 |
| lineColor | string | '#0960bd' | 单波形默认颜色 |
| width / height | number | 自适应 | 组件总尺寸,单位为 CSS 像素 |
| zoomable / showTooltip | boolean | true / true | 缩放和数值 tooltip 开关 |
| pannable | boolean | false | 空格拖拽平移开关 |
| minZoomSpan | number | 未设置 | 最小缩放跨度,使用原始 X 数据单位 |
| minVisiblePoints | number | 0 | 缩放后至少保留的不同 X 坐标数 |
| maxZoomScale | number \| null | 未设置 | 最大缩放倍数;null 表示不限制 |
| initialXDomain | [number, number] | 未设置 | 所有图框的初始 X 范围(可超出数据,空白显示) |
| initialXDomains | Record<string, [number, number]> | 未设置 | 按 track/series ID 配置初始范围(可超出数据) |
| xDomainStrategy | WaveformXDomainStrategy | { type: 'data' } | 自动 X 轴视口范围策略 |
| yDomain | [number, number] | 未设置 | 所有波形的固定 Y 轴范围 |
| yDomains | Record<string, [number, number]> | 未设置 | 按 track/series ID 配置固定范围 |
| grid | WaveformGridOptions | { rowCount: 2, columnCount: 1, showPagination: true, fillIncompleteLastRow: false } | 网格和分页 |
| axes | WaveformAxesOptions | 轴线均显示 | X/Y 轴基线、Y 轴分割数与 X 轴 label 格式化 |
| rendering | WaveformRenderingOptions | {} | 降采样与点/误差棒间距 |
| plotMargin | WaveformPlotMargin | { top: 18, bottom: 52 } | 绘图区上下边距,单位为 CSS 像素 |
| title / frameStyle | 对应公开类型 | 未设置 | 标题和图框样式 |
| legend | WaveformLegendOptions | { position: 'top-right', orientation: 'auto' } | 图例位置、排列、背景和交互 |
| frameNumber | string \| number | 未设置 | 图框水印内容 |
| frameNumbers | Record<string, string \| number> | 未设置 | 按 trackId 覆盖图框水印内容 |
| zeroLine | WaveformZeroLineOptions | { visible: false } | 零值参考线显隐与样式 |
| cleanView | boolean | false | 保留波形、图框和刻度的净图模式 |
| presentationMode | boolean | false | 禁用绘图区交互的展示模式 |
| annotations | WaveformAnnotation[] | [] | 受控标注数据 |
| annotationsVisible | boolean | true | 标注图层显隐 |
| interactionMode | 'zoom' \| 'annotation' | 'zoom' | 左键交互模式 |
| hiddenSeriesIds | string[] | 未设置 | 受控隐藏系列 ID |
| defaultHiddenSeriesIds | string[] | [] | 非受控模式的初始隐藏系列 |
所有公开类型均可从包入口导入,例如 WaveformData、WaveformSeries、TypedSampleData、
TypedPointData、WaveformAnnotation、WaveformLineStyle、WaveformRenderingOptions、
WaveformPlotMargin、
WaveformAxesOptions、WaveformXAxisLabelFormatter、WaveformXDomainStrategy、WaveformZeroLineOptions、
WaveformGridOptions 和 WaveformGridTrackLines。
数据结构
单通道可以使用采样值(sampleRate 为每秒采样数)或显式坐标点:
import type { WaveformData } from 'waveform-analysis'
const samples: WaveformData = {
kind: 'samples',
values: [0.2, 0.4, 0.1],
sampleRate: 1000,
startTime: 0,
}
const points: WaveformData = {
kind: 'points',
points: [
{ x: 0, y: 12 },
{ x: 0.001, y: 15, lowerError: 0.4, upperError: 0.8 },
],
}也可使用紧凑的 TypedArray 输入。typed-samples 只保存 Y 值,X 始终按
startTime + index / sampleRate(秒)计算;typed-points 使用 Float64Array 存储 X,Y 和
误差字段可使用 Float32Array 或 Float64Array。所有 typed-points 字段长度必须一致;不支持
其他 TypedArray 类型。选择 Float32Array 的 Y 或误差值时,精度损失由调用方承担。输入数据和
底层 ArrayBuffer 仅被读取,不会排序、写入或转移所有权。
const compactSamples: WaveformData = {
kind: 'typed-samples',
values: new Float32Array([0.2, 0.4, 0.1]),
sampleRate: 1000,
}
const compactPoints: WaveformData = {
kind: 'typed-points',
x: new Float64Array([0, 0.001]),
y: new Float32Array([12, 15]),
lowerError: new Float32Array([0.4, 0.4]),
upperError: new Float32Array([0.8, 0.8]),
}TypedArray 输入在内部保持紧凑列表示。typed-samples 只复制 Y 值及采样元数据;遇到无效样本
时用紧凑源索引保留时间空洞,不生成完整 X 列。既有数组式渲染接口通过有界缓存按需构造单个
WaveformPoint,Worker 消息使用私有数值副本,不会分离调用方的 ArrayBuffer。
多通道使用 kind: 'series'。同一个 trackId 的系列会绘制在同一图框中;没有
trackId 的系列默认各占一个图框。建议为每个系列提供全图唯一且稳定的 id。
const chartData: WaveformData = {
kind: 'series',
series: [
{ id: 'ch-a', shotNo: '13300', name: '通道 A', trackId: 'group-1', data: samples },
{ id: 'ch-b', shotNo: '13300', name: '通道 B', trackId: 'group-1', data: points },
],
}需要让图框在合并或暂时没有数据时仍保持位置和分页,可传入 grid.trackOrder。列表中的
trackId 会按给定顺序占用图框;没有对应系列的项显示为空图框,数据中未列出的轨道会追加在末尾。传入
hideEmptyTracks: true 可省略空图框,同时按稳定 trackId 保留图例、交互和水印映射:
<WaveformChart :data="chartData" :grid="{ rowCount: 2, trackOrder: ['ch-a', 'ch-b', 'ch-c'] }" />波形图
组件内置缩放、悬浮取点和 tooltip。调用方只需要提供波形数据:
<script setup lang="ts">
import { WaveformChart } from 'waveform-analysis'
import 'waveform-analysis/style.css'
</script>
<template>
<WaveformChart :data="chartData" />
</template>固定振幅上下限
未传入 Y 轴范围时,组件继续根据当前可见系列及其误差棒自动计算范围。传入 yDomain
后,所有波形使用同一个固定范围;超出范围的部分只在绘图区裁剪,不会过滤或修改原始数据:
<WaveformChart :data="chartData" :y-domain="[-80, 80]" />多通道可以通过 yDomains 按稳定的 trackId 或 seriesId 分别配置:
<WaveformChart
:data="chartData"
:y-domain="[-100, 100]"
:y-domains="{
voltage: [-65, 65],
current: [-260, 260],
}"
/>范围优先级为 trackId 配置、seriesId 配置、全局 yDomain、数据自动范围。上下限必须
是两个有限且不相等的数字;倒序范围会自动调整为升序,无效配置会回退到下一优先级。
固定范围作为 Y 轴刻度算法的初始范围;轴会按漂亮步长经过 D3 的 nice() 扩展,确保主刻度
等间距。原始数据和传入的范围值不会被修改。
单值轴叠加模式会合并同一根轴上所有可见系列的有效范围;多值轴模式按系列分别使用配置, 超过四根轴后复用第 4 根轴的系列会取范围并集。隐藏系列不参与公共范围合并。
固定范围存在时,对应图框的 Y 轴不会被平移或视口重置覆盖;X 轴缩放、平移和重置保持原有
行为。运行时更新或移除 yDomain / yDomains 会立即重新布局,移除后恢复自动范围。
Y 轴自动等分
Y 轴默认显示 5 个主刻度(包含上下端点),并使用类似 ECharts 的 1 / 2 / 5 × 10ⁿ 漂亮
步长,保证主刻度之间数值等间距。可以通过 axes.y.splitNumber 指定主刻度总数;每条
Y 轴会独立计算:
<WaveformChart :data="chartData" :axes="{ y: { splitNumber: 5 } }" />默认会将 Y 轴范围扩展为便于读取的等距刻度。传入 nice: false 可保持数据自动范围或
yDomain / yDomains 的原始端点:
<WaveformChart :data="chartData" :axes="{ y: { nice: false } }" />缩放后按可视区间加载数据
组件支持 Plotly 风格的矩形框选缩放:在 zoom 模式下按住鼠标左键拖拽,松开后同时缩放
X/Y 轴;设置 pannable 后,指针位于图表内时按住空格键拖拽可平移当前视口。
鼠标滚轮可放大和缩小,双击恢复完整视口。
组件会在真实滚轮或框选确定目标范围时同步触发 zoom-intent,可立即取消过时请求并启动
新请求;缩放结束后仍会触发 zoom-end。独立分图模式还会包含 trackIndex 和稳定的
seriesIds。
<WaveformChart
ref="chart"
:data="chartData"
:initial-x-domain="initialDomain"
:min-zoom-span="initialDomainSpan / 40"
pannable
@zoom-intent="loadVisibleData"
@zoom-reset="restoreInitialData"
/>zoom-change 会在滚轮、框选和平移过程中触发,适合更新外部状态。后端按视口回填数据时,
应使用 zoom-intent 使每次用户意图立即使旧请求失效;zoom-end 适合只在手势完成后执行
的工作。标注数据应由父组件独立持有,替换波形数据时不要清空标注,组件会根据当前数据域
自动隐藏或恢复对应标注。
zoom-end.gesture 用于区分 wheel 和 box。单轨道 payload 使用 yStart/yEnd;共享
X 轴且包含多个轨道时使用按稳定 track ID 索引的 yRanges。平移不会触发 zoom-end,
因此不会自动发起新的区间加载请求。
调用方应处理加载失败的情况(网络错误、超时等),并保持旧数据或显示加载状态。生产环境建议使用
AbortController 取消过时的请求。
initialXDomain 固定首次及重置时的 X 轴视口范围,不要将它改成后端返回的当前窗口;范围可以超出数据的实际时间,超出部分保留空白。独立图框有不同时间范围时,可通过
initialXDomains 按 track ID 或 series ID 分别配置。minZoomSpan 使用原始 X 数据单位,
可防止每次区间数据回填后重新累计放大。未配置任何缩放约束时保留既有的 40 倍兜底;设置
minVisiblePoints: 2 可缩放到两个真实采样点,maxZoomScale: null 可显式关闭倍率上限。
显式设置多种限制时采用最严格的一项。双击图框会
重置组件内部缩放并触发 zoom-reset;调用方应在事件中取消区间请求并恢复首次完整数据。
外部重置按钮也可以通过模板引用调用组件公开的 resetViewport() 方法,然后执行相同的数据恢复逻辑。
宿主在替换局部数据后如果需要恢复之前保存的 X 视口,可以调用公开的
setViewportDomain(domain, trackIndex?)。传入范围使用原始秒坐标,组件会按当前数据边界、
xDomainStrategy、minZoomSpan、minVisiblePoints 和 maxZoomScale 重新约束;无效范围会被忽略。共享
X 轴模式直接传入一个范围,独立模式可以按实际 trackIndex 分别设置(省略 trackIndex
时应用到当前所有图框):
chartRef.value?.setViewportDomain(previousDomain)
chartRef.value?.setViewportDomain(trackDomain, trackIndex)建议在 data 引用更新后调用该方法。组件也会在数据引用变更时重投影当前内部视口,避免新
数据边界使旧 transform 失效;resetViewport(trackIndex?) 的既有行为保持不变。
没有显式配置初始范围时,可以通过 xDomainStrategy 将数据范围扩展为便于阅读的视口端点。
默认的 { type: 'data' } 保持数据最小值和最大值不变;type: 'nice' 使用固定刻度数量计算
易读边界,且只扩展视口,不修改原始点位、tooltip、标注或缩放事件值:
<WaveformChart
:data="chartData"
:x-domain-strategy="{ type: 'nice', bounds: 'end', tickCount: 10, includeExplicit: true }"
/>例如秒坐标数据范围为 [0, 4.999999] 且 timeUnit='ms' 时,上述配置会使用
[0, 5] 作为初始及重置视口,两端 label 显示 0 和 5000。bounds: 'end'
只扩展右端;默认的 bounds: 'both'
会同时扩展两端。tickCount 默认为 10,只参与边界计算,不随组件宽度变化。
initialXDomains、initialXDomain 的显式配置默认保持原值;仅当 includeExplicit: true 时
也应用 nice 扩展。独立模式在未配置显式范围时
按图框分别计算,共享 X 轴模式则合并所有可见图框后计算。所有范围仍使用原始秒坐标,
timeUnit 只影响显示。
独立坐标模式下,回填响应应只替换 seriesIds 对应的系列,并调用
resetViewport(trackIndex);其他图框的数据和缩放状态应保持不变。独立模式下双击图框触发的
zoom-reset payload 会包含该图框的 trackIndex 和 seriesIds;共享 X 范围模式的 payload
不包含这两个字段,表示全局复位。忽略事件参数的既有监听器可以继续使用。
多通道数据应为每个 WaveformSeries 提供稳定的 id。内部时间坐标始终使用秒,
timeUnit 只控制坐标轴和 tooltip 的显示单位。
Tooltip 每个系列按 炮号:通道 (x:值 y:值) 格式显示。WaveformSeries.shotNo 为空或未提供时,
炮号显示为“未配置炮号”;Tooltip 不显示单位和误差附加文本。
纵轴单位
可在每个 WaveformSeries 上设置可选的 unit,单位会显示在对应 Y 轴的顶部刻度中:
const chartData = {
kind: 'series',
series: [
{
id: 'voltage',
name: '电压',
unit: 'V',
data: {
kind: 'points',
points: [
{ x: 0, y: 1200 },
{ x: 1, y: 3000 },
],
},
},
],
} satisfies WaveformData当 Y 轴使用科学计数法时,顶部刻度格式为 E+03 (V) 3;未使用科学计数法时,格式为
(V) 3。单位为空、全为空白或未配置时,不显示 (单位),例如仍显示为 E+03 3 或 3。
多 Y 轴模式下,每根轴使用该轴首个 series 的单位。
线型、点型与误差棒
每条序列可以独立设置连线方式、数据点符号和误差棒:
const series = {
id: 'temperature',
shotNo: '13300',
name: '温度',
lineType: 'step-end',
lineStyle: 'dashed',
pointType: 'circle',
errorBar: { visible: true, width: 1.5, capWidth: 8 },
data: {
kind: 'points',
points: [
{ x: 0, y: 12, error: 0.5 },
{ x: 1, y: 15, lowerError: 0.4, upperError: 0.8 },
],
},
} satisfies WaveformSerieslineType 支持 none、linear、step-start、step-middle 和 step-end;它控制连接线的几何形态,兼容值
step-after 与 step-end 等价。三个阶梯值分别在区间起点、中点和终点跳变。pointType
支持 none、circle、square、triangle 和 diamond。默认使用普通直线且不显示数据点;
lineStyle 控制连接线的描边样式,支持 solid、dashed 和 dash-dot,默认值为 solid;
设置 lineType: 'none' 可以隐藏数据点之间的连接线,只保留点符号和误差棒;将其改为
linear 或阶梯类型即可同时显示对应连接线。误差棒仅在 errorBar.visible 为 true 时显示,
并参与 Y 轴范围计算;当误差棒可见时,lineType 和 pointType 可以同时为 none,用于展示
纯误差棒。只有连接线、点符号和误差棒全部关闭时才会回退为普通直线。lowerError、
upperError 分别覆盖对称的 error,图例会同步显示实际线型、点型和误差棒样式。
叠加与多值轴
为多条曲线设置相同的 trackId,可将它们叠加到同一图框。overlayMode 控制叠加
曲线共享一根 Y 轴还是使用独立值轴:
<WaveformChart :data="chartData" display-mode="independent" overlay-mode="multi-axis" />overlayMode 对应公开类型 WaveformOverlayMode,可选值为 single-axis 和
multi-axis,默认值为 single-axis。多值轴最多渲染四根 Y 轴;超过四条曲线时,
后续曲线复用第 4 根轴,该轴的范围覆盖绑定到它的全部曲线。轴顺序依次为左侧、
右侧;三轴时第 3 根位于右侧外部,四轴时顺序为左侧、左侧外部、右侧、右侧外部。
overlayMode 与 displayMode 相互独立。displayMode 仍可使用 independent、
separated 或 compact 控制图框布局和 X 轴共享方式;未共享 trackId 的单曲线
图框不会因为切换叠加方式而改变。
绘图区域尺寸
width 和 height 接收像素数值,并且可以独立设置。指定的维度使用固定尺寸,未指定的
维度自适应填满父容器:
<div class="chart-container">
<WaveformChart :data="chartData" :width="960" />
</div>
<style scoped>
.chart-container {
height: 520px;
}
</style>自适应高度要求父容器具有明确高度;父容器未定高时,组件使用最低 180px 高度。
显式高度同样保留 180px 下限。非有限尺寸按未指定处理,负宽度归零。
图表标题
title 在整个波形网格上方渲染一次,支持显隐、对齐、字体样式和旋转:
<WaveformChart
:data="chartData"
:title="{
visible: true,
text: 'Shot:4712',
align: 'center',
textStyle: {
color: '#1f2937',
fontSize: 14,
fontFamily: '"Microsoft YaHei", "微软雅黑", sans-serif',
rotation: 0,
fontWeight: 400,
fontStyle: 'normal',
textDecoration: 'none',
letterSpacing: '1px',
},
}"
/>对应的公开类型为 WaveformTitleOptions 和 WaveformTitleTextStyle。未传 title、
visible 为 false,或 text 去除首尾空格后为空时,标题不渲染且不占高度。标题默认
居中、字号 14px、颜色 #1f2937、微软雅黑、常规字重且不旋转。标题区域高度按文字及旋转角度
在 44px 至 160px 之间计算;超长文字会省略,悬浮可查看完整内容。
width 和 height 始终表示组件总尺寸。标题显示后会从总高度中扣除标题区域,剩余高度
用于 SVG 绘图区,因此启用标题不会扩大组件或破坏父容器布局。
Demo 左侧控制面板提供标题实时预览,可配置标题名称、显隐、对齐、字体、字号、粗体、
斜体、下划线、旋转和颜色。字号范围为 8–72px,旋转范围为 -180–180°;样式栏中的
A 用于恢复常规字重、非斜体和无下划线,关闭标题不会清除已经填写的配置。
图框样式
frameStyle 统一设置所有非空图框的边框和背景,颜色支持带 alpha 的 CSS 颜色值:
<WaveformChart
:data="chartData"
:frame-style="{
borderColor: 'rgba(31, 41, 55, 0.8)',
borderWidth: 2,
borderStyle: 'dashed',
backgroundColor: 'rgba(14, 165, 233, 0.08)',
}"
/>frameStyle.borderStyle 支持 solid(实线)、dashed(虚线)和 dotted(点虚线)。
对应的公开类型为 WaveformFrameStyle。默认边框颜色为 #1f2937、线宽为 1、线型为
solid,背景透明。borderWidth 为 0 时隐藏边框;非有限值或负数会回退到默认线宽。
图例与曲线显隐
每个图框独立管理自己的图例:图框内有两条或更多曲线时显示图例,只有一条曲线时不显示。
legend.backgroundColor 设置图例的背景颜色。该字段接受任意有效 CSS 颜色值,
可通过 rgba(...) 或 hsla(...) 中的 alpha 通道调整透明度:
<script setup lang="ts">
import { ref } from 'vue'
const hiddenSeriesIds = ref<string[]>([])
</script>
<WaveformChart
:data="chartData"
v-model:hidden-series-ids="hiddenSeriesIds"
:legend="{
position: 'top-right',
trackPositions: {
'group-1': 'top-left',
'group-2': 'bottom',
},
orientation: 'auto',
backgroundColor: 'rgba(255, 255, 255, 0.45)',
interactive: true,
}"
/>legend.trackPositions 按图框 ID 单独覆盖图例位置;同一个 trackId 组成的图框以该
trackId 为键,未设置 trackId 时以规范化后的 series.id 为键。未命中的图框继续使用
legend.position,两者都未配置时使用 top-right。当 orientation 为 auto 时,每个图框
会根据最终位置独立选择排列方向:top、bottom 为水平排列,其余位置为垂直排列。
未配置或传入空字符串时,图例背景默认使用 rgba(255, 255, 255, 0.7)。
legend.interactive 默认为 false;开启后可以单击或使用键盘操作图例项切换曲线显隐。
调用方可通过 hiddenSeriesIds 和 update:hidden-series-ids 控制状态,也可使用
defaultHiddenSeriesIds 设置非受控模式的初始隐藏项。隐藏状态同步作用于坐标轴、tooltip、
悬浮点和标注交互;允许隐藏全部曲线,并可通过保留的图例恢复显示。
显隐状态以规范化后的 series.id 为键。要在数据刷新和重新排序后稳定保留状态,每个系列都应
提供全图唯一且稳定的显式 id;自动生成的索引 ID 或重复 ID 添加的后缀不保证跨排序稳定。
零值参考线与净图
zeroLine 用于绘制 y = 0 的水平参考线,默认隐藏。参考线只在对应 Y 轴的当前 domain
包含 0 时渲染,不会为了显示参考线而扩展数据范围。多值轴模式下,每根可见 Y 轴分别按自身
scale 定位零线:
<WaveformChart
:data="chartData"
:zero-line="{
visible: true,
color: '#98a2b3',
width: 1,
dash: '6 4',
}"
/>dash 直接对应 SVG 的 stroke-dasharray;传入空字符串可显示实线。无效或非正数的
width 会回退到 1。
设置 cleanView 后,组件保留波形、图框边框、X/Y 轴刻度及刻度值,并隐藏标题内容、图例、
网格、轴标签、图框背景、帧水印、零值参考线、标注和分页器。原图的标题区域、边距和波形
尺寸保持不变;缩放、悬浮、十字线和 tooltip 仍然可用,切换回普通模式后原有配置和标注
不会丢失:
<WaveformChart :data="chartData" :clean-view="cleanViewEnabled" />网格、分页与交互模式
grid 控制独立图框的行列数(范围 1–10)以及是否显示分页器。默认值为 2 行、
1 列并开启分页;当图框数量超过网格容量时,分页器会显示在图表右下角。
fillIncompleteLastRow 默认关闭。开启后,分页容量仍由 rowCount * columnCount 决定;
最后一页或未满页会移除没有数据的整行,并将最后一行的实际图框等宽铺满可用宽度。例如 2 列
网格的最后一行只有一个图框时,该图框会占满整行。完整页和关闭该选项时的布局保持不变。
还可以通过 trackLines 按轨道 ID 分别控制水平/垂直网格线的显隐和颜色。颜色未配置时,
继续使用组件默认的主/次网格颜色:
<WaveformChart
:data="chartData"
:grid="{
rowCount: 2,
columnCount: 2,
showPagination: true,
fillIncompleteLastRow: true,
trackLines: {
voltage: {
horizontal: false,
vertical: true,
verticalColor: '#2563eb',
},
},
}"
:interaction-mode="interactionMode"
/>axes 可以分别隐藏 X/Y 轴的基线,同时保留刻度短线、刻度数字、科学计数倍率、单位和轴标题。
与关闭网格线、设置 frameStyle 组合后,可以只使用图框边框围住绘图区:
<WaveformChart
:data="chartData"
:axes="{
x: { lineVisible: false },
y: { lineVisible: false },
}"
:grid="{
trackLines: {
voltage: { horizontal: false, vertical: false },
},
}"
:frame-style="{
borderColor: '#1f2937',
borderWidth: 1,
borderStyle: 'solid',
}"
/>X 轴刻度和左右端点默认先按 timeUnit 转换为秒或毫秒,再显示为无千分位、无科学计数法的
完整普通十进制值,不会四舍五入为整数。可通过 axes.x.labelFormatter 对显示值做运算和格式化:
<WaveformChart
:data="chartData"
:axes="{
x: {
labelFormatter: (value, context) =>
`${context.kind === 'tick' ? '' : '[' + context.kind + '] '}${(value / 1000).toFixed(3)}`,
},
}"
/>formatter 首参是已按 timeUnit 换算的数值;上下文包含 kind(tick、start 或 end)、
原始 rawValue、timeUnit、原始可视域 domain 和换算后的 displayDomain。formatter 只决定
label 文本,不改变刻度位置、源数据、缩放域或事件中的原始 X 坐标。
Demo 左侧“网格与轴线”支持直接切换数值或固定时间格式。数值模式可设置运算倍率和小数位数;
固定时间模式输出 YYYY-MM-DD HH:mm:ss[.SSS],可选择中国标准时间、本地时区或 UTC,并控制
是否显示毫秒。时间戳数据仍按组件的秒坐标契约传入,使用默认毫秒显示单位时 formatter 会收到
可直接传给 Date 的毫秒时间戳。
interactionMode 可选 zoom 或 annotation,默认使用缩放模式。右键绘图区可直接打开
标注编辑器,无需切换交互模式。zoomable、pannable 和 showTooltip 可分别控制缩放、
空格拖拽平移和 tooltip;平移默认关闭。
展示场景可启用 presentationMode,统一禁用绘图区的 tooltip、缩放、平移、双击复位和
标注交互。该模式不会隐藏任何图形内容,也不会禁用图例切换或分页;关闭后恢复原交互配置。
<WaveformChart :data="chartData" :presentation-mode="true" />空数据或过滤后没有有效点时,组件会保留图框布局并显示“暂无有效波形数据”。
大数据渲染
组件按不可变数据处理:替换 data 引用会重新过滤、排序和缓存坐标域,并重置视口;
原地修改已有数组不会触发缓存刷新。建议通过 shallowRef 保存大数据并整体替换引用。
规范化始终保留所有有效点,坐标域、误差棒、tooltip 和标注均使用完整数据;绘制路径会根据
当前视口和 rendering 配置自动降采样。调用方如需在传入组件前主动压缩数据,应自行保留
原始数据,以免影响 tooltip、标注和误差范围的精度。
默认 auto 模式按每条系列的当前可见点数决定渲染路径:不超过 1,000 个点直接使用原始
可视点;超过 1,000 个点时,组件将当前页面的可见系列合并为一次 Web Worker 请求,并在
Worker 内通过 WASM 执行保峰采样。默认采样数量由图框宽度和每像素最大点数控制,默认每个像素
最多渲染 4 个保峰点。多通道场景可通过 sampling.maxPointCount 设定每条曲线的固定目标数量;
设置后会优先于像素密度。可按业务调整:
<WaveformChart
:data="chartData"
:rendering="{
downsample: true,
downsampleThreshold: 2000,
maxPointsPerPixel: 4,
pointMinSpacing: 10,
errorBarMinSpacing: 12,
}"
/>全量视图只绘制均匀分布的真实数据点,放大后会自动恢复更多源标记。pointMinSpacing 和
errorBarMinSpacing 分别控制点符号和误差棒的最小水平间距,单位为 CSS 像素。两者同时
显示时共用一批采样点,并采用两个间距中的较大值,确保误差棒与对应点符号保持共心;仅显示
一类装饰时仍使用各自的间距。仅显示一类装饰时可将对应间距设为 0;两者同时显示时需将
两个间距都设为 0 才会关闭共同限制。设置 downsample: false 会关闭曲线和装饰的全部降采样。
rendering.sampling 控制 Worker/WASM 渲染路径。默认模式为 auto,其单系列可见点阈值为
1_000,默认策略为 peak。wasm 强制发起 Worker+WASM 请求,不受阈值影响;raw 完全绕过
Worker 和采样,直接渲染当前可见原始点。sampling.maxPointCount 用于每条线的固定采样数量,
sampling.maxPointsPerPixel 用于按宽度自适应;两者同时存在时固定数量优先。嵌套的
sampling.maxPointsPerPixel 优先于旧的 rendering.maxPointsPerPixel。未指定 sampling.mode 时,旧的 downsample: false 映射为
raw,downsample: true 映射为 auto。
rendering: {
sampling: {
mode: 'auto',
autoThreshold: 1_000,
autoHysteresis: 0,
strategy: 'peak',
maxPointCount: 1_000,
maxPointsPerPixel: 4,
rawPointLimit: 100_000,
wasmFailureFallback: 'error',
},
}Worker 或 WASM 初始化不可用时,auto 会报告诊断后使用等价 JavaScript 采样,避免把大数据
退回为超长 SVG 路径。强制 wasm 模式遵循 wasmFailureFallback:'error' 保留已有有效路径
或当前同步渲染回退,并发出错误;'javascript' 会在错误诊断后显示 JavaScript 等价结果。快速
缩放或数据替换会用 requestId 和数据 revision 丢弃过期结果;等待新结果时保留当前有效路径。
采样结果只覆盖 SVG 线条点,完整原始数据仍用于 domain、tooltip、最近点、标注、误差范围和缩放
约束。组件卸载和 data 引用替换会终止实例 Worker 并释放其数据集。
高级场景可直接使用独立的 sampleWaveformWasm 数值内核。它接收等长的扁平 Float64Array
X/Y 坐标,返回真实源点的 Uint32Array 索引,或 average / sum 的新 X/Y 数组。该 API 不会
修改输入:
import { sampleWaveformWasm } from 'waveform-analysis'
const result = await sampleWaveformWasm({
x: new Float64Array([0, 1, 2, 3]),
y: new Float64Array([4, 1, 8, 3]),
strategy: 'peak',
targetPointCount: 4,
})none、peak、lttb、min、max 和 minmax 返回源索引;average 与 sum 返回合成
坐标。calculateWasmRange 与 findWasmVisibleRange 分别提供完整范围统计和规范化序列上的
半开可见索引范围查询。WASM 初始化失败时这些调用会拒绝,由调用方选择是否使用 JavaScript
参考实现回退。
降采样仅作用于 SVG 中的曲线、点符号和误差棒。点符号和误差棒在每个系列中分别合并为 单个 SVG path;最近点查询、tooltip、标注插值、Y 轴误差范围和受控数据不会损失精度。
采样点标注
标注由父组件通过 v-model:annotations 持有,标注使用 seriesId 和 x/y 数据坐标,
不依赖数组下标:
<script setup lang="ts">
import { ref } from 'vue'
import {
parseWaveformAnnotations,
serializeWaveformAnnotations,
WaveformChart,
type WaveformAnnotation,
type WaveformInteractionMode,
} from 'waveform-analysis'
import 'waveform-analysis/style.css'
const annotations = ref<WaveformAnnotation[]>([])
const annotationsVisible = ref(true)
const interactionMode = ref<WaveformInteractionMode>('zoom')
</script>
<template>
<WaveformChart
:data="chartData"
v-model:annotations="annotations"
:annotations-visible="annotationsVisible"
:interaction-mode="interactionMode"
/>
</template>标注默认显示。右键绘图区任意位置即可弹出居中编辑器,标注会吸附到当前 X 位置最近的真实采样点,右键已有标注可以编辑或删除。
标注框可以直接拖动进行手动避让,拖动只改变标签框位置,不会改变 x/y 数据锚点;偏移会以 labelOffsetX/labelOffsetY 像素字段保存在标注中。标注文本最多 40 个字符,边框色、文字色和背景色均支持取色与透明度调整。组件只负责内存中的受控数据,
业务层负责会话或后端持久化。
标注可以序列化为带版本号的 JSON,并在解析成功后整体替换当前数据:
const exportedJson = serializeWaveformAnnotations(annotations.value)
async function importAnnotationFile(file: File) {
annotations.value = parseWaveformAnnotations(await file.text())
}导出格式为 { version: 1, annotations: [...] }。解析会验证全部标注;文件格式、版本或任意
字段无效时会抛出 TypeError,不会返回部分结果。导入包含未知 seriesId 的标注是允许的,
对应曲线加载后会恢复显示。文件选择、错误提示和下载由业务层实现。
X 轴刻度和左右端点先按 timeUnit 转换为秒或毫秒,再显示为不带分组符和科学计数法的完整普通十进制值,并可通过 axes.x.labelFormatter 自定义。Y 轴会根据完整显示域选择格式:最大绝对值在 [0.01, 100) 时显示两位普通小数;大于等于 100,或大于 0 且小于 0.01 时,刻度显示两位缩放值,并在顶部刻度单独显示共享倍率 E±NN。如果 series 配置了 unit,顶部刻度会在倍率后追加 (单位);没有科学计数法时也会显示 (单位),多 Y 轴分别使用对应轴首个 series 的单位。tooltip 使用最多 4 位小数的本地化普通数字并省略无意义尾零;标注编辑器的 X 坐标跟随 timeUnit 并固定 3 位小数,Y 坐标显示完整普通十进制。所有格式化都只发生在展示层,内部坐标值保持原始精度。
标注框默认布局在采样点正上方,只做绘图区边界裁剪;文本框通过连接箭头指向标注位置,多个标注重叠时可通过拖动手动避让。
事件
组件提供以下事件,名称与 Vue 模板写法一致:
| 事件 | 说明 |
| --------------------------------------------------------------- | -------------------------------------------------------------- |
| point-hover | 当前最近点变化时触发,离开图表时传入 null |
| zoom-intent | 真实滚轮或框选确定目标范围时同步触发,参数含端点和 gesture |
| zoom-change | 缩放过程中触发,参数为 [start, end] |
| zoom-end | 滚轮或框选结束后触发;gesture 区分二者,独立模式附带轨道信息 |
| zoom-reset | 双击重置视口时触发;独立模式 payload 标识目标图框 |
| page-change | 分页变化,参数为当前页和总页数 |
| series-visibility-change | 图例切换曲线显隐时触发 |
| annotation-create / annotation-update / annotation-delete | 标注新增、更新或删除 |
| sampling-complete | 每条系列当前采样结果的 WaveformSamplingDiagnostics |
| sampling-backend-change | 某系列在 raw、javascript 或 wasm 后端之间切换时触发 |
| sampling-error | Worker/WASM 不可用或强制 WASM 无法满足时的降级/失败信息 |
annotations 和 hidden-series-ids 支持 v-model;annotations-visible 与
interaction-mode 是受控输入属性。业务层应负责将标注和显隐状态持久化。
sampling-complete 的 payload 包含模式、实际后端、策略、完整/可视/渲染点数、耗时、请求号、
revision,以及 scheduledRequestCount、coalescedRequestCount 和
maxPendingRequestCount 三个调度指标;适合性能面板或日志采集。sampling-error 不携带原始
Error 对象,只提供可序列化的消息、请求模式、回退方式和受影响系列 ID。不要将这些诊断事件
作为 pointermove 处理逻辑。
多分辨率索引、缓存与诊断
Worker 的 JavaScript 回退后端和 Rust/WASM 后端都按需建立多分辨率索引。peak、min、max
和 minmax 使用可追溯到真实源点的 Min/Max 分层;average 与 sum 使用可重用的 Sum/Count
分层,LTTB 仍按当前视口即时计算。WASM 数据集通过可释放 handle 持久保存;每个桶的左右边界按
当前可见半开索引范围精确计算,所以局部缩放不会复用首次全局采样而丢失细节。
连续 wheel、pan 和 resize 使用 latest-wins 调度:同一图表最多保留一个执行中任务和一个最新
待处理任务,中间视口会被合并,Worker 消息队列不会随输入事件无界增长。过期请求仍通过
requestId 和 revision 双重校验,不能覆盖最终稳定视口。
每个已访问数据集的索引默认最多使用 8 MiB。采样输出缓存使用 LRU,默认最多 96 个条目或
16 MiB;先到达任一上限就逐出最久未使用结果。缓存键包括 dataset ID、revision、可见起止索引、
图框宽度、目标密度、策略、后端及线型/点型/误差棒/装饰间距等渲染维度。替换、删除或清空数据集会
同步释放其索引与缓存项。sampling-complete.cacheHit 只在 Worker 实际返回该缓存项时为 true。
演示可在 #/wasm-sampling 打开。该路由生成 10 条各 100,000 点的 Float32Array 通道,可切换
auto / wasm / raw,所有采样策略与自动阈值;Peak 类策略按每像素点数采样,LTTB、Average、
Sum 按每条线的目标渲染点数采样。右侧表格显示每个系列的真实后端、
源点/可见点/渲染点、耗时和缓存命中状态。
库产物会把 WASM 数据内联到 Worker 运行时,消费者无需额外复制 .wasm 文件。严格 CSP 部署
仍需允许库的模块 Worker,并允许浏览器编译 WebAssembly;若策略禁止 data: Worker/WASM 资源
或 WebAssembly 编译,auto 可回退到 JavaScript,而强制 wasm 会按配置报告失败。当前自动化
与浏览器烟测覆盖 Chromium;Firefox、Safari、严格 CSP、离线和子路径部署应按
docs/performance-guide.md 的发布清单验证。
项目结构
src/index.ts:组件库公开入口和工具函数导出src/components/WaveformChart.vue:公共组件边界,连接图表控制器与视图src/components/WaveformChartView.vue:图表视图、交互宿主和渲染层编排src/components/core/useWaveformChartController.ts:布局、状态和交互控制器编排src/components/{core,data,rendering,interaction,annotation}:数据、布局、渲染和交互模块src/App.vue:综合可交互 demo,src/data中提供示例波形数据src/router.ts:Demo 路由;src/views/FixedYDomainDemo.vue与src/views/WasmSamplingDemo.vue为专用示例wasm/Cargo.toml、wasm/src/lib.rs:Rust/WASM 数值内核源码;wasm/pkg是提交到 Git 的 WASM 绑定与二进制产物,wasm/target是本地 Rust 构建缓存,不提交到 Git
本地开发
开发环境要求 Node.js 22、pnpm 10.32.1、Rust 1.95(含 wasm32-unknown-unknown target)和 wasm-pack
0.14.0:
pnpm install
pnpm dev常用质量检查和构建命令:
pnpm typecheck
pnpm lint:oxlint
pnpm lint
pnpm lint:all
pnpm format:check
pnpm test
pnpm test:coverage
pnpm buildpnpm lint:oxlint 使用 Oxlint 的默认 correctness 检查及内置 TypeScript、Unicorn 和 Oxc
插件,自动忽略 dist/、dist-demo/、coverage/ 和 node_modules/。pnpm lint 继续负责
ESLint 的 Vue SFC、TypeScript ESLint 和 max-lines 规则;pnpm lint:all 会依次运行两者。
pnpm format:check 只读检查 Prettier 格式,pnpm format 保持原有的写入行为。
pnpm build 同时生成 dist/ 组件库产物和 dist-demo/ 演示应用。正式公开入口为
src/index.ts,样式入口为 src/styles.css;dist/ 和 dist-demo/ 均为生成目录,不要手工编辑。
