Files
waveform-analysis/README.md
liqiyuan 3ed17eb061
All checks were successful
Package component / package (push) Successful in 5m34s
feat(chart): add axis and dotted frame controls
2026-07-22 21:22:26 +08:00

28 KiB
Raw Blame History

Waveform Analysis

基于 Vue 3、TypeScript 和 D3 的响应式 SVG 波形图组件。适合展示单通道、多通道和大规模采样 数据内置缩放、tooltip、图例、误差棒、标注、分页和多 Y 轴叠加。

当前稳定版本:v0.1.15

组件使用不可变数据模型:替换 data 引用后会重新计算数据域和视口;大数据会按当前可见范围 和屏幕像素自动保峰降采样,而 tooltip、最近点查询和标注仍使用完整原始数据。

在线示例

最新稳定版 Demohttps://lqycustomsite.online/waveform-analysis/

特性

  • Vue 3 Composition API + TypeScript支持按需导入 WaveformChart
  • 采样值、显式坐标点和多系列数据模型
  • independentseparatedcompact 三种布局模式
  • 曲线、阶梯线、点符号和对称/非对称误差棒
  • 实线、虚线和点划线,可按系列独立配置
  • 缩放过程事件、缩放结束按可视区间加载和视口重置
  • 可选的空格拖拽平移,默认关闭并隔离多图表实例
  • 多系列图例、受控显隐、网格分页和最多四根 Y 轴
  • 按轨道控制水平/垂直网格线的显隐与颜色
  • 受控标注、右键编辑、拖拽避让和自定义颜色
  • 标题、图框、坐标轴、零值参考线、净图和渲染参数可配置

安装

组件库将 Vue、D3、Ant Design Vue 和 vue3-colorpicker 作为 peer dependency直接安装到业务项目时请一并 安装这些依赖:

pnpm add waveform-analysis vue d3 ant-design-vue vue3-colorpicker

运行时版本要求

组件库支持以下运行时版本:

依赖 支持版本
Vue >=3.2.33 <4
Ant Design Vue >=3.2.20 <4
D3 >=7.9.0 <8
vue3-colorpicker >=2.3.0 <3

安装时请确保业务项目中的 peer dependency 版本均满足上述范围。

发布

发布由推送版本 tag 触发。package.jsonversion 必须与 tag 去掉 v 后完全一致。 稳定版使用 vX.Y.Z,预发布版使用 vX.Y.Z-rc.1;稳定版发布为 npm latest,预发布版发布为 next。流水线会创建 Gitea Release并上传包文件与 SHA-256 校验文件。

最小示例

<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>

父容器需要有明确高度;未指定 widthheight 时,组件会填充父容器,并保持最小高度 180pxWaveformChart 的正式入口为 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 坐标数
initialXDomain [number, number] 未设置 所有图框的初始 X 范围
initialXDomains Record<string, [number, number]> 未设置 按 track/series ID 配置初始范围
grid WaveformGridOptions { rowCount: 2, columnCount: 1, showPagination: true } 网格和分页
axes WaveformAxesOptions 轴线均显示 X/Y 轴基线显隐
rendering WaveformRenderingOptions {} 降采样与点/误差棒间距
title / legend / frameStyle 对应公开类型 未设置 标题、图例和图框样式
frameNumber string | number 未设置 图框水印内容
zeroLine WaveformZeroLineOptions { visible: false } 零值参考线显隐与样式
cleanView boolean false 仅保留波形的净图模式
annotations WaveformAnnotation[] [] 受控标注数据
annotationsVisible boolean true 标注图层显隐
interactionMode 'zoom' | 'annotation' 'zoom' 左键交互模式
hiddenSeriesIds string[] 未设置 受控隐藏系列 ID
defaultHiddenSeriesIds string[] [] 非受控模式的初始隐藏系列

所有公开类型均可从包入口导入,例如 WaveformDataWaveformSeriesWaveformAnnotationWaveformLineStyleWaveformRenderingOptionsWaveformAxesOptionsWaveformZeroLineOptionsWaveformGridOptionsWaveformGridTrackLines

数据结构

单通道可以使用采样值(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 },
  ],
}

多通道使用 kind: 'series'。同一个 trackId 的系列会绘制在同一图框中;没有 trackId 的系列默认各占一个图框。建议为每个系列提供全图唯一且稳定的 id

const chartData: WaveformData = {
  kind: 'series',
  series: [
    { id: 'ch-a', name: '通道 A', trackId: 'group-1', data: samples },
    { id: 'ch-b', name: '通道 B', trackId: 'group-1', data: points },
  ],
}

波形图

组件内置缩放、悬浮取点和 tooltip。调用方只需要提供波形数据

<script setup lang="ts">
import { WaveformChart } from 'waveform-analysis'
import 'waveform-analysis/style.css'
</script>

<template>
  <WaveformChart :data="chartData" />
</template>

缩放后按可视区间加载数据

组件支持 Plotly 风格的矩形框选缩放:在 zoom 模式下按住鼠标左键拖拽,松开后同时缩放 X/Y 轴;设置 pannable 后,指针位于图表内时按住空格键拖拽可平移当前视口。 鼠标滚轮仍可放大,双击恢复完整视口。 组件会在滚轮或框选缩放结束后触发 zoom-end,调用方可以使用端点请求后端,再通过 data 传回新数据。独立分图模式还会包含 trackIndex 和稳定的 seriesIds

<WaveformChart
  ref="chart"
  :data="chartData"
  :initial-x-domain="initialDomain"
  :min-zoom-span="initialDomainSpan / 40"
  pannable
  @zoom-end="loadVisibleData"
  @zoom-reset="restoreInitialData"
/>

zoom-change 会在滚轮、框选和平移过程中触发,适合更新外部状态;后端请求应使用 zoom-end,或在 zoom-change 上自行防抖。标注数据应由父组件独立持有,替换波形数据时 不要清空标注,组件会根据当前数据域自动隐藏或恢复对应标注。

zoom-end.gesture 用于区分 wheelbox。单轨道 payload 使用 yStart/yEnd;共享 X 轴且包含多个轨道时使用按稳定 track ID 索引的 yRanges。平移不会触发 zoom-end 因此不会自动发起新的区间加载请求。

调用方应处理加载失败的情况(网络错误、超时等),并保持旧数据或显示加载状态。生产环境建议使用 AbortController 取消过时的请求。

initialXDomain 固定首次完整数据的 X 轴缩放边界,不要将它改成后端返回的当前窗口;独立图框有不同时间范围时,可通过 initialXDomains 按 track ID 或 series ID 分别配置。minZoomSpan 使用原始 X 数据单位, 可防止每次区间数据回填后重新累计放大。双击图框会 重置组件内部缩放并触发 zoom-reset;调用方应在事件中取消区间请求并恢复首次完整数据。 外部重置按钮也可以通过模板引用调用组件公开的 resetViewport() 方法,然后执行相同的数据恢复逻辑。

独立坐标模式下,回填响应应只替换 seriesIds 对应的系列,并调用 resetViewport(trackIndex);其他图框的数据和缩放状态应保持不变。

多通道数据应为每个 WaveformSeries 提供稳定的 id。内部时间坐标始终使用秒, timeUnit 只控制坐标轴和 tooltip 的显示单位。

线型、点型与误差棒

每条序列可以独立设置连线方式、数据点符号和误差棒:

const series = {
  id: 'temperature',
  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 WaveformSeries

lineType 支持 nonelinearstep-startstep-middlestep-end;它控制连接线的几何形态,兼容值 step-afterstep-end 等价。三个阶梯值分别在区间起点、中点和终点跳变。pointType 支持 nonecirclesquaretrianglediamond。默认使用普通直线且不显示数据点; lineStyle 控制连接线的描边样式,支持 soliddasheddash-dot,默认值为 solid 设置 lineType: 'none' 可以隐藏数据点之间的连接线,只保留点符号和误差棒;将其改为 linear 或阶梯类型即可同时显示对应连接线。误差棒仅在 errorBar.visibletrue 时显示, 并参与 Y 轴范围计算;当误差棒可见时,lineTypepointType 可以同时为 none,用于展示 纯误差棒。只有连接线、点符号和误差棒全部关闭时才会回退为普通直线。lowerErrorupperError 分别覆盖对称的 error,图例会同步显示实际线型、点型和误差棒样式。

叠加与多值轴

为多条曲线设置相同的 trackId,可将它们叠加到同一图框。overlayMode 控制叠加 曲线共享一根 Y 轴还是使用独立值轴:

<WaveformChart :data="chartData" display-mode="independent" overlay-mode="multi-axis" />

overlayMode 对应公开类型 WaveformOverlayMode,可选值为 single-axismulti-axis,默认值为 single-axis。多值轴最多渲染四根 Y 轴;超过四条曲线时, 后续曲线复用第 4 根轴,该轴的范围覆盖绑定到它的全部曲线。轴顺序依次为左侧、 右侧;三轴时第 3 根位于右侧外部,四轴时顺序为左侧、左侧外部、右侧、右侧外部。

overlayModedisplayMode 相互独立。displayMode 仍可使用 independentseparatedcompact 控制图框布局和 X 轴共享方式;未共享 trackId 的单曲线 图框不会因为切换叠加方式而改变。

绘图区域尺寸

widthheight 接收像素数值,并且可以独立设置。指定的维度使用固定尺寸,未指定的 维度自适应填满父容器:

<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',
    },
  }"
/>

对应的公开类型为 WaveformTitleOptionsWaveformTitleTextStyle。未传 titlevisiblefalse,或 text 去除首尾空格后为空时,标题不渲染且不占高度。标题默认 居中、字号 14px、颜色 #1f2937、微软雅黑、常规字重且不旋转。标题区域高度按文字及旋转角度 在 44px160px 之间计算;超长文字会省略,悬浮可查看完整内容。

widthheight 始终表示组件总尺寸。标题显示后会从总高度中扣除标题区域,剩余高度 用于 SVG 绘图区,因此启用标题不会扩大组件或破坏父容器布局。

Demo 左侧控制面板提供标题实时预览,可配置标题名称、显隐、对齐、字体、字号、粗体、 斜体、下划线、旋转和颜色。字号范围为 872px,旋转范围为 -180180°;样式栏中的 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,背景透明。borderWidth0 时隐藏边框;非有限值或负数会回退到默认线宽。

图例与曲线显隐

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。当 orientationauto 时,每个图框 会根据最终位置独立选择排列方向:topbottom 为水平排列,其余位置为垂直排列。

未配置或传入空字符串时,图例背景默认使用 rgba(255, 255, 255, 0.7)legend.interactive 默认为 false;开启后可以单击或使用键盘操作图例项切换曲线显隐。 调用方可通过 hiddenSeriesIdsupdate: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 后,组件隐藏标题内容、图例、网格、坐标轴、轴标签、图框背景与边框、帧水印、 零值参考线、标注和分页器,同时保留原图的标题区域、边距和波形尺寸。缩放、悬浮、十字线和 tooltip 仍然可用,切换回普通模式后原有配置和标注不会丢失:

<WaveformChart :data="chartData" :clean-view="cleanViewEnabled" />

网格、分页与交互模式

grid 控制独立图框的行列数(范围 110)以及是否显示分页器。默认值为 2 行、 1 列并开启分页;当图框数量超过网格容量时,分页器会显示在图表右下角。

还可以通过 trackLines 按轨道 ID 分别控制水平/垂直网格线的显隐和颜色。颜色未配置时, 继续使用组件默认的主/次网格颜色:

<WaveformChart
  :data="chartData"
  :grid="{
    rowCount: 2,
    columnCount: 2,
    showPagination: 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',
  }"
/>

interactionMode 可选 zoomannotation,默认使用缩放模式。右键绘图区可直接打开 标注编辑器,无需切换交互模式。zoomablepannableshowTooltip 可分别控制缩放、 空格拖拽平移和 tooltip平移默认关闭。 空数据或过滤后没有有效点时,组件会保留图框布局并显示“暂无有效波形数据”。

大数据渲染

组件按不可变数据处理:替换 data 引用会重新过滤、排序和缓存坐标域,并重置视口; 原地修改已有数组不会触发缓存刷新。建议通过 shallowRef 保存大数据并整体替换引用。 规范化始终保留所有有效点坐标域、误差棒、tooltip 和标注均使用完整数据;绘制路径会根据 当前视口和 rendering 配置自动降采样。调用方如需在传入组件前主动压缩数据,应自行保留 原始数据,以免影响 tooltip、标注和误差范围的精度。

默认在可见点超过 2,000 时进行降采样,每个像素最多渲染 4 个保峰点。可按业务调整:

<WaveformChart
  :data="chartData"
  :rendering="{
    downsample: true,
    downsampleThreshold: 2000,
    maxPointsPerPixel: 4,
    pointMinSpacing: 10,
    errorBarMinSpacing: 12,
  }"
/>

全量视图只绘制均匀分布的真实数据点,放大后会自动恢复更多源标记。pointMinSpacingerrorBarMinSpacing 分别控制点符号和误差棒的最小水平间距,单位为 CSS 像素。两者同时 显示时共用一批采样点,并采用两个间距中的较大值,确保误差棒与对应点符号保持共心;仅显示 一类装饰时仍使用各自的间距。仅显示一类装饰时可将对应间距设为 0;两者同时显示时需将 两个间距都设为 0 才会关闭共同限制。设置 downsample: false 会关闭曲线和装饰的全部降采样。

降采样仅作用于 SVG 中的曲线、点符号和误差棒。点符号和误差棒在每个系列中分别合并为 单个 SVG path最近点查询、tooltip、标注插值、Y 轴误差范围和受控数据不会损失精度。

采样点标注

标注由父组件通过 v-model:annotations 持有,标注使用 seriesIdx/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、Y 轴会根据各自完整显示域选择格式:最大绝对值在 [0.01, 100) 时显示两位普通小数;大于等于 100,或大于 0 且小于 0.01 时,刻度显示两位缩放值,并在轴末端单独显示共享倍率 E±NN。X 轴先按 timeUnit 转换为秒或毫秒再判断范围,多 Y 轴则分别计算倍率。tooltip 使用最多 4 位小数的本地化普通数字;标注编辑器的 X 坐标跟随 timeUnit 并固定 3 位小数Y 坐标显示完整普通十进制。所有格式化都只发生在展示层,内部坐标值保持原始精度。 标注框默认布局在采样点正上方,只做绘图区边界裁剪;文本框通过连接箭头指向标注位置,多个标注重叠时可通过拖动手动避让。

事件

组件提供以下事件,名称与 Vue 模板写法一致:

事件 说明
point-hover 当前最近点变化时触发,离开图表时传入 null
zoom-change 缩放过程中触发,参数为 [start, end]
zoom-end 滚轮或框选结束后触发;gesture 区分二者,独立模式附带轨道信息
zoom-reset 双击重置视口时触发,调用方应恢复首次完整数据
page-change 分页变化,参数为当前页和总页数
series-visibility-change 图例切换曲线显隐时触发
annotation-create / annotation-update / annotation-delete 标注新增、更新或删除

annotationshidden-series-ids 支持 v-modelannotations-visibleinteraction-mode 是受控输入属性。业务层应负责将标注和显隐状态持久化。

项目结构

  • src/index.ts:组件库公开入口和工具函数导出
  • src/components/WaveformChart.vue图表容器、缩放、tooltip、图例和标注编排
  • src/components/{core,data,rendering,interaction,annotation}:数据、布局、渲染和交互模块
  • src/App.vue:可交互 demosrc/data 中提供示例波形数据

本地开发

开发环境要求 Node.js 22 和 pnpm

pnpm install
pnpm dev

常用质量检查和构建命令:

pnpm typecheck
pnpm lint
pnpm test
pnpm test:coverage
pnpm build

pnpm build 同时生成 dist/ 组件库产物和 dist-demo/ 演示应用。正式公开入口为 src/index.ts,样式入口为 src/styles.cssdist/dist-demo/ 均为生成目录,不要手工编辑。

发布流程

发布由推送版本 tag 触发。先将 package.jsonversion 更新为目标版本并提交,再创建同版本 tag

git tag -a v0.1.15 -m "Release v0.1.15"
git push origin main --follow-tags

支持稳定版 vX.Y.Z 与预发布版 vX.Y.Z-rc.1。tag 去掉 v 后必须与 package.jsonversion 完全一致。稳定版发布为 npm latest,预发布版发布为 npm next。流水线会创建 Gitea Release并上传 .tgz 与 SHA-256 校验文件。