npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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
  • 采样值、显式坐标点和多系列数据模型
  • independentseparatedcompact 三种布局模式
  • 曲线、阶梯线、点符号和对称/非对称误差棒
  • 实线、虚线和点划线,可按系列独立配置
  • 缩放过程事件、缩放结束按可视区间加载和视口重置
  • 可选的空格拖拽平移,默认关闭并隔离多图表实例
  • 多系列图例、受控显隐、网格分页和最多四根 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>

父容器需要有明确高度;未指定 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 坐标数 | | 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[] | [] | 非受控模式的初始隐藏系列 |

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

数据结构

单通道可以使用采样值(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 和 误差字段可使用 Float32ArrayFloat64Array。所有 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 按稳定的 trackIdseriesId 分别配置:

<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 用于区分 wheelbox。单轨道 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?)。传入范围使用原始秒坐标,组件会按当前数据边界、 xDomainStrategyminZoomSpanminVisiblePointsmaxZoomScale 重新约束;无效范围会被忽略。共享 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 显示 05000bounds: 'end' 只扩展右端;默认的 bounds: 'both' 会同时扩展两端。tickCount 默认为 10,只参与边界计算,不随组件宽度变化。 initialXDomainsinitialXDomain 的显式配置默认保持原值;仅当 includeExplicit: true 时 也应用 nice 扩展。独立模式在未配置显式范围时 按图框分别计算,共享 X 轴模式则合并所有可见图框后计算。所有范围仍使用原始秒坐标, timeUnit 只影响显示。

独立坐标模式下,回填响应应只替换 seriesIds 对应的系列,并调用 resetViewport(trackIndex);其他图框的数据和缩放状态应保持不变。独立模式下双击图框触发的 zoom-reset payload 会包含该图框的 trackIndexseriesIds;共享 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 33。 多 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 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 左侧控制面板提供标题实时预览,可配置标题名称、显隐、对齐、字体、字号、粗体、 斜体、下划线、旋转和颜色。字号范围为 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,背景透明。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 后,组件保留波形、图框边框、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 换算的数值;上下文包含 kindtickstartend)、 原始 rawValuetimeUnit、原始可视域 domain 和换算后的 displayDomain。formatter 只决定 label 文本,不改变刻度位置、源数据、缩放域或事件中的原始 X 坐标。

Demo 左侧“网格与轴线”支持直接切换数值或固定时间格式。数值模式可设置运算倍率和小数位数; 固定时间模式输出 YYYY-MM-DD HH:mm:ss[.SSS],可选择中国标准时间、本地时区或 UTC,并控制 是否显示毫秒。时间戳数据仍按组件的秒坐标契约传入,使用默认毫秒显示单位时 formatter 会收到 可直接传给 Date 的毫秒时间戳。

interactionMode 可选 zoomannotation,默认使用缩放模式。右键绘图区可直接打开 标注编辑器,无需切换交互模式。zoomablepannableshowTooltip 可分别控制缩放、 空格拖拽平移和 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,
  }"
/>

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

rendering.sampling 控制 Worker/WASM 渲染路径。默认模式为 auto,其单系列可见点阈值为 1_000,默认策略为 peakwasm 强制发起 Worker+WASM 请求,不受阈值影响;raw 完全绕过 Worker 和采样,直接渲染当前可见原始点。sampling.maxPointCount 用于每条线的固定采样数量, sampling.maxPointsPerPixel 用于按宽度自适应;两者同时存在时固定数量优先。嵌套的 sampling.maxPointsPerPixel 优先于旧的 rendering.maxPointsPerPixel。未指定 sampling.mode 时,旧的 downsample: false 映射为 rawdownsample: 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,
})

nonepeaklttbminmaxminmax 返回源索引;averagesum 返回合成 坐标。calculateWasmRangefindWasmVisibleRange 分别提供完整范围统计和规范化序列上的 半开可见索引范围查询。WASM 初始化失败时这些调用会拒绝,由调用方选择是否使用 JavaScript 参考实现回退。

降采样仅作用于 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 轴刻度和左右端点先按 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 | 某系列在 rawjavascriptwasm 后端之间切换时触发 | | sampling-error | Worker/WASM 不可用或强制 WASM 无法满足时的降级/失败信息 |

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

sampling-complete 的 payload 包含模式、实际后端、策略、完整/可视/渲染点数、耗时、请求号、 revision,以及 scheduledRequestCountcoalescedRequestCountmaxPendingRequestCount 三个调度指标;适合性能面板或日志采集。sampling-error 不携带原始 Error 对象,只提供可序列化的消息、请求模式、回退方式和受影响系列 ID。不要将这些诊断事件 作为 pointermove 处理逻辑。

多分辨率索引、缓存与诊断

Worker 的 JavaScript 回退后端和 Rust/WASM 后端都按需建立多分辨率索引。peakminmaxminmax 使用可追溯到真实源点的 Min/Max 分层;averagesum 使用可重用的 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.vuesrc/views/WasmSamplingDemo.vue 为专用示例
  • wasm/Cargo.tomlwasm/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 build

pnpm 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.cssdist/dist-demo/ 均为生成目录,不要手工编辑。