@radishcandy/h-charts

Framework-agnostic Highcharts plugins with optional Vue 3 and React host adapters for industrial analysis.
339
Install
npm install @radishcandy/h-charts
Latest Version:1.0.0
Vue:3
License:MIT
Last Updated:Sep 24, 2026
Links: Gitee  ·  npm
Maintainer: radishcandy

hCharts

面向工业监测与设备状态分析场景的 Highcharts 通用封装与插件库。

hCharts 将图表渲染、交互分析和宿主框架适配分层设计:核心能力保持框架无关,可用于原生 TypeScript;Vue 3 与 React 适配层只负责生命周期、DOM 容器、事件转发和实例暴露。这样,同一套频谱、波形和趋势分析能力可以复用于 Vue、React、Web Components 或其他宿主环境。

项目仓库:Gitee yolanda624/h-charts。npm 包名为 @radishcandy/h-charts。

适用场景

  • 设备运行状态监测
  • 旋转机械振动分析
  • 频谱、包络谱和时域波形展示
  • 传感器趋势曲线与历史数据回放
  • 多测点、多工况数据对比
  • 故障特征频率、阈值和边频诊断
  • 工业监控大屏和分析工作台

核心能力

能力 说明
统一图表实例 封装 Highcharts 初始化、默认配置、主题变量和自适应重绘
Cursor 光标 数据点吸附、键盘移动、拖动和光标标签格式化
多图联动 多个测点或不同采样间隔图表之间同步 X 值
缩放与平移 框选缩放、右键平移、恢复完整数据范围
频谱分析 边频线、阈值线和特征频率标记
数据标注 标记当前点、显示读数、拖动标注和批量清理
右键菜单 插件开关、分析参数输入、子菜单和 PNG 导出
动态数据 增量更新 Series 数据或 Highcharts 配置,不必重建插件
宿主适配 提供原生 TypeScript API,以及独立 Vue 3、React 组件入口

设计原则

核心插件保持框架无关

插件本身不依赖 Vue、React、Element Plus、项目路由、全局状态或业务 i18n。插件通过 IPlugin 接口与 HChart 交互,任何宿主都可以创建、启用、禁用和销毁插件。

Vue 和 React 只做适配层

HChartView 不会把所有插件重写成 Vue 组件,只负责:

  • 在组件挂载后创建 HChart
  • 提供图表 DOM 容器
  • 转发 cursor-change、chart-click 等事件
  • 暴露 setData、setCursorX、updateOptions 等实例方法
  • 在组件卸载时销毁图表、插件和事件监听

React 的 HChartView 使用相同的核心实例和插件,只将以上职责映射为 DOM ref、effect 清理、回调和 forwardRef 暴露的方法。

面向工业数据的稳定更新

实时数据或历史回放场景应优先调用 setSeriesData(),配置变化再调用 updateOptions()。这样可以保留现有光标、分析线和菜单插件状态,减少频繁销毁与重建造成的闪烁。

安装

npm 安装

npm install highcharts @radishcandy/h-charts

highcharts 是 peer dependency。Vue 3 与 React 只会在对应的 /vue、/react 子路径被引用时作为可选 peer dependency 使用。

Gitee 源码安装

git clone https://gitee.com/yolanda624/h-charts.git
cd hCharts
npm install

原生 TypeScript 使用

核心入口不导入 Vue,适合工业监测大屏、原生前端页面或其他框架适配层:

import { HChart, CursorPlugin, ZoomPlugin, ContextMenuPlugin } from '@radishcandy/h-charts'

const chart = new HChart({
  container: document.querySelector<HTMLElement>('#spectrum')!,
  options: {
    xAxis: { type: 'linear', title: { text: 'Frequency (Hz)' } },
    yAxis: { title: { text: 'Amplitude' } },
    series: [{ type: 'line', name: 'Sensor A', data: [[0, 8], [1, 15], [2, 12]] }]
  },
  plugins: [new CursorPlugin(), new ZoomPlugin(), new ContextMenuPlugin({ items: ['sideband', 'threshold_line', 'export'] })]
})

chart.setSeriesData([[0, 10], [1, 18], [2, 13]])
chart.updateOptions({ title: { text: 'Sensor A - running condition 01' } })
chart.destroy()

请在容器已经挂载到浏览器 DOM 后创建 HChart。服务端渲染环境可以安全导入模块,但必须在客户端生命周期中构造图表。

其他宿主环境

Web Components、Angular、Svelte 或业务自研运行时无需复制图表逻辑:在宿主的“挂载”钩子中使用容器元素创建 HChart,把宿主回调封装为一个 IPlugin,并在“卸载”钩子中调用 destroy()。这样所有分析插件、光标联动和更新 API 都与原生 TypeScript、Vue、React 保持一致。

Cursor 多图联动

工业监测中常见“原始波形 + 包络谱”“测点 A + 测点 B”“不同采样间隔数据”的同步查看。CursorLinkPlugin 只转发源图表的 X 值,目标图表会根据自身数据找到最近点,因此不要求两组数据长度或采样间隔相同。

双向联动时,两个插件共享一个 CursorSyncState,避免事件在两个图表之间递归触发:

import { HChart, CursorPlugin, CursorLinkPlugin, createCursorSyncState } from '@radishcandy/h-charts'

const syncState = createCursorSyncState()
let waveform: HChart
let spectrum: HChart

waveform = new HChart({
  container: document.querySelector<HTMLElement>('#waveform')!,
  options: { series: [{ type: 'line', name: 'Waveform', data: [[0, 3], [10, 8], [20, 5]] }] },
  plugins: [new CursorPlugin(), new CursorLinkPlugin({ name: 'waveform-to-spectrum', syncState, target: () => spectrum })]
})

spectrum = new HChart({
  container: document.querySelector<HTMLElement>('#spectrum')!,
  options: { series: [{ type: 'line', name: 'Spectrum', data: [[0, 9], [8, 4], [16, 12], [24, 7]] }] },
  plugins: [new CursorPlugin(), new CursorLinkPlugin({ name: 'spectrum-to-waveform', syncState, target: () => waveform })]
})

两张图都需要 CursorPlugin;CursorLinkPlugin 是独立的联动桥接插件。一个图表添加多个联动方向时,请为每个实例设置不同的 name。

Vue 3 使用

从 Vue 子路径导入适配组件。组件只负责挂载、销毁、事件转发和实例暴露:

<script setup lang="ts">
import { ref } from 'vue'
import type * as Highcharts from 'highcharts'
import { CursorPlugin } from '@radishcandy/h-charts'
import { HChartView } from '@radishcandy/h-charts/vue'

const chartRef = ref<InstanceType<typeof HChartView> | null>(null)
const options: Highcharts.Options = {
  xAxis: { title: { text: 'Frequency (Hz)' } },
  yAxis: { title: { text: 'Amplitude' } },
  series: [{ type: 'line', data: [[0, 2], [1, 8], [2, 4]] }]
}
const plugins = [new CursorPlugin()]

function refresh() {
  chartRef.value?.setData([[0, 5], [1, 3], [2, 9]])
}
</script>

<template>
  <section style="height: 320px">
    <HChartView ref="chartRef" :options="options" :plugins="plugins" @cursor-change="(point) => console.log('cursor', point)" />
  </section>
  <button type="button" @click="refresh">刷新测点数据</button>
</template>

需要让 Vue 深度监听配置时设置 :update-on-options-change="true"。对于高频采集数据,优先使用暴露的 setData(),避免因响应式对象变化而频繁更新完整配置。

React 使用

React 适配器位于独立的 /react 子路径;它只负责 DOM ref、effect 生命周期和事件回调,图表及插件始终由框架无关的 HChart 承载。

import { useRef } from 'react'
import type * as Highcharts from 'highcharts'
import { CursorPlugin } from '@radishcandy/h-charts'
import { HChartView, type HChartReactHandle } from '@radishcandy/h-charts/react'

const options: Highcharts.Options = {
  series: [{ type: 'line', data: [[0, 2], [1, 8], [2, 4]] }]
}
const plugins = [new CursorPlugin()]

export function Spectrum() {
  const chartRef = useRef<HChartReactHandle | null>(null)

  return (
    <>
      <section style={{ height: 320 }}>
        <HChartView
          ref={chartRef}
          options={options}
          plugins={plugins}
          onCursorChange={(point) => console.log('cursor', point)}
        />
      </section>
      <button onClick={() => chartRef.current?.setData([[0, 5], [1, 3], [2, 9]])}>
        刷新测点数据
      </button>
    </>
  )
}

设置 updateOnOptionsChange 后,React 中新的 options 对象会调用 Highcharts update();高频数据请通过 ref 调用 setData()、updateOptions() 或 requestReflow(),避免重建图表和插件。可直接参考 React 示例,Vue 3 的可复制示例位于 Vue 3 示例。

插件与生命周期

所有插件遵循统一生命周期:

  1. init(chart):保存图表实例,并根据 enabled 配置决定是否启用。
  2. enable():创建分析图形并注册事件。
  3. disable():移除分析图形和事件监听,但保留插件实例。
  4. destroy():终止插件,释放全部资源。
chart.togglePlugin('ThresholdLinePlugin', true)
chart.togglePlugin('ThresholdLinePlugin', false)
chart.removePlugin('ThresholdLinePlugin')

可用插件包括:

  • CursorPlugin:光标、吸附、键盘移动和拖动
  • CursorLinkPlugin:多图 X 值联动
  • ZoomPlugin:缩放、平移和恢复范围
  • SidebandPlugin:边频线及边频数量调整
  • ThresholdLinePlugin:可拖动阈值线,并标记超过阈值的数据点
  • AnnotationPlugin:测点标注、标签和连接线
  • ContextMenuPlugin:右键菜单、插件开关、参数输入和导出

插件参数与方法

所有插件都实现 IPlugin:init(chart)、enable()、disable()、destroy()、update(options) 和可选的 isEnabled()。HChart 创建时会调用 init();除非传入 enabled: false,插件默认启用。运行时可使用 chart.togglePlugin(name, enabled),或先用 chart.getPlugin(name) 取得插件实例后调用下列专用方法。

插件 配置接口 主要专用方法
CursorPlugin CursorPluginOptions updateCursor()
CursorLinkPlugin CursorLinkPluginOptions onCursorChange()(自动调用)
ZoomPlugin ZoomPluginOptions 无(使用图表鼠标手势)
SidebandPlugin SidebandPluginOptions setLineCount()、getLineCount()、setDelta()、getDelta()、setDirectShow()
ThresholdLinePlugin ThresholdLinePluginOptions setValue()、getValue()
AnnotationPlugin AnnotationPluginOptions markCurrentPoint()、markPoint()、clearMark()、getMarkedPoint()、getMarkedPoints()
ContextMenuPlugin ContextMenuPluginOptions、ContextMenuItem BUILTIN_ITEMS、DEFAULT_ITEMS

以下各节列出每个配置字段的默认值、行为和完整公开方法。

CursorPlugin
interface CursorPluginOptions {
  enabled?: boolean             // 默认 true
  multiLine?: boolean           // 默认 true;是否为多条可见 Series 绘制读数
  draggable?: boolean           // 默认 true;是否允许 Shift+拖动光标线
  labelFormatter?: (x: number, y: number) => string
  onCursorMove?: (point: { x: number; y: number; index: number }) => void
}

用途:点击图表背景或数据点时,将 X 吸附到第一条 Series 的最近数据点;支持左右方向键、拖动光标线、缩放后自动恢复,以及在多条 Series 上显示同一 X 的读数。labelFormatter 自定义光标标签文本,onCursorMove 在点击、拖动或键盘移动完成后回调。

公开方法:

  • updateCursor(xValue: number):按 X 值查找最近点并绘制光标,同时触发 chart.setCursor() 和 onCursorMove。
  • enable() / disable():绑定或移除键盘、鼠标和缩放事件。
  • isEnabled():返回当前启用状态(继承自 BasePlugin)。
const cursor = new CursorPlugin({
  draggable: true,
  multiLine: false,
  labelFormatter: (x, y) => `${x.toFixed(2)} Hz / ${y.toFixed(3)}`,
  onCursorMove: (point) => console.log('cursor', point)
})
const chart = new HChart({ container: '#chart', options, plugins: [cursor] })
chart.setCursorX(42) // 等价于 cursor.updateCursor(42)

此外导出了两个点击抑制辅助函数:suppressCursorClick(container, ms?) 用于在拖动后短暂屏蔽点击,shouldSuppressCursorClick(container) 用于检查屏蔽状态。

CursorLinkPlugin
interface CursorLinkPluginOptions {
  enabled?: boolean             // 默认 true
  target: () => IHChart | null | undefined
  name?: string                 // 默认 CursorLinkPlugin
  syncState?: CursorSyncState   // 双向联动时两端共享
}
interface CursorSyncState { active: boolean }

用途:只转发源图表的 point.x,目标图表通过自己的数据重新吸附,因此允许两张图采样间隔不同。target 应返回目标 HChart,使用函数可以支持两张图初始化顺序不固定;目标不存在时会安全跳过。

公开方法:onCursorChange(point) 在源图表光标变化时自动调用;enable()、disable()、isEnabled() 和 destroy() 继承自 BasePlugin。双向联动必须共享 createCursorSyncState() 返回的对象,以避免递归触发。

ZoomPlugin
interface ZoomPluginOptions {
  enabled?: boolean             // 默认 true
}

用途:左键框选使用 Highcharts 原生 X 轴缩放;右键拖动执行 X 轴平移;从右向左拖动可恢复完整数据范围。插件会自动处理拖动后的点击抑制,并将视图限制在 dataMin 到 dataMax 内。

公开方法:enable()、disable()、destroy() 和 isEnabled()。缩放方向、滚轮缩放等基础行为由 HChart 默认 chart.zoomType = 'x' 与 zooming.mouseWheel 配置控制,可通过 chart.updateOptions() 覆盖。

SidebandPlugin
interface SidebandPluginOptions {
  enabled?: boolean             // 默认 true
  initialDelta?: number         // 默认 0;首次启用时按 X 轴范围 / 20 计算
  lineCount?: number            // 默认 5;在中心频率两侧各绘制 N 条
  directShow?: boolean          // 默认 false;无光标时也允许保持启用
  color?: string                // 默认 green
  dashStyle?: Highcharts.DashStyleValue // 默认 Dash
  labelColor?: string           // 默认跟随 color
  xAxisUnit?: 'Hz' | 'ms' | string // 默认 Hz;ms 会换算为 1000 / delta
  onDeltaChange?: (delta: number) => void
}

插件依赖 CursorPlugin 提供中心点。启用后以当前光标 X 为中心绘制 ±delta、±2*delta ... 边频线,最近的两条线显示标签;拖动边频线会更新 delta。

公开方法:

  • setLineCount(count) / getLineCount():设置或读取每侧边频线数量。
  • setDelta(delta) / getDelta():设置或读取边频间隔;设置时立即重绘并触发 onDeltaChange。
  • setDirectShow(show):切换直接显示模式。
  • isEnabled()、enable()、disable()、destroy():插件状态与生命周期。
const sideband = new SidebandPlugin({ lineCount: 3, initialDelta: 5, color: '#16a34a' })
chart.addPlugin(sideband)
sideband.setDelta(2.5)
sideband.setLineCount(4)
ThresholdLinePlugin
interface ThresholdLinePluginOptions {
  enabled?: boolean             // 默认 true
  initialValue?: number         // 默认 0;启用时按 Y 轴范围中点初始化
}

用途:在 Y 轴绘制可拖动的水平阈值线,并为超过阈值的第一条 Series 数据点添加序号标记。拖动透明命中区域或可见线即可实时改变阈值。

公开方法:setValue(value) 设置阈值并立即重绘;getValue() 读取当前阈值;以及继承的 enable()、disable()、isEnabled()、destroy()。禁用时会移除阈值线、命中区域和数据点标记。

const threshold = new ThresholdLinePlugin({ initialValue: 1.2 })
chart.addPlugin(threshold)
threshold.setValue(0.8)
console.log(threshold.getValue())
AnnotationPlugin
interface AnnotationPluginOptions {
  enabled?: boolean             // 默认 true
  labelFormatter?: (x: number, y: number) => string
  markerRadius?: number         // 默认 2
  markerColor?: string          // 默认 #ff4d4f
}

用途:将当前光标点或任意 Point 标记为可拖动注释,绘制标记点、读数标签和连接线;图表重绘、缩放后会自动更新注释位置。

公开方法:

  • markCurrentPoint():标记 chart.activePoint(没有时使用插件收到的最近光标点)。
  • markPoint(point):标记指定 { x, y, index, seriesIndex? } 点。
  • clearMark():清除全部标记。
  • getMarkedPoint():返回最后一个标记,未标记时返回 null。
  • getMarkedPoints():返回所有标记的副本。
  • update(options):合并并应用 markerRadius、markerColor、labelFormatter。
const annotations = new AnnotationPlugin({
  markerColor: '#f97316',
  labelFormatter: (x, y) => `${x.toFixed(1)}, ${y.toFixed(2)}`
})
chart.addPlugin(annotations)
annotations.markCurrentPoint()
annotations.markPoint({ x: 10, y: 3.2, index: 10 })
ContextMenuPlugin
interface ContextMenuPluginOptions {
  enabled?: boolean             // 默认 true
  items?: ContextMenuEntry[]    // 默认 sideband、threshold_line、band_count、export
  zIndex?: number               // 默认 10000
  labels?: Partial<Record<ContextMenuBuiltinId, string>>
  translate?: (id: ContextMenuBuiltinId, fallback: string) => string
}

右键打开固定定位菜单;菜单实例之间互斥,点击外部自动关闭。内置项为 sideband(边频开关)、threshold_line(阈值线开关)、band_count(边频数量输入)和 export(PNG 导出)。菜单中的插件项不存在时会自动创建对应的禁用插件实例。

自定义菜单项类型如下:

interface ContextMenuItem {
  label?: string
  id?: string
  type?: 'builtin' | 'custom'
  pluginName?: string
  action?: (chart: IHChart, item?: ContextMenuItem) => void
  items?: ContextMenuItem[]       // 子菜单
  inputType?: 'text' | 'number'
  value?: string | number
  min?: number
  max?: number
  onInput?: (value: string, chart: IHChart) => void
  fileName?: string | ((chart: IHChart) => string)
  exportBackgroundColor?: string
  exportOptions?: Record<string, unknown>
  exportChartOptions?: Record<string, unknown>
}

公开方法:ContextMenuPlugin.BUILTIN_ITEMS 和 .DEFAULT_ITEMS 返回内置菜单定义;enable()、disable()、destroy()、isEnabled() 控制菜单生命周期。推荐使用 createSpectrumContextMenuItems()、createEnvelopeContextMenuItems() 或 createWaveContextMenuItems() 作为宿主项目的起点,再传入自定义项或 labels/translate 完成本地化。

BasePlugin 与 IPlugin

若要扩展插件,可继承 BasePlugin 并实现 name。BasePlugin 会保存 chart、维护 enabled 状态,并提供 isEnabled();重写 enable() 时注册事件,重写 disable()/destroy() 时必须成对清理。自定义插件至少需要实现:

interface IPlugin {
  name: string
  init(chart: IHChart): void
  destroy(): void
  enable(): void
  disable(): void
  update(options: unknown): void
  isEnabled?(): boolean
  onChartClick?(event: Highcharts.PointerEventObject): void
  onCursorChange?(point: Point): void
}

右键菜单与本地化

ContextMenuPlugin 内置以下 ID:

sideband
threshold_line
band_count
export

业务系统可以通过 labels 或 translate 注入中文、英文及其他语言,不需要把业务 i18n 依赖带入核心插件:

new ContextMenuPlugin({
  items: ['sideband', 'threshold_line', 'export'],
  labels: { sideband: '显示边频', threshold_line: '阈值线', export: '导出图表' }
})

菜单主题支持以下 CSS 变量:

:root {
  --hchart-primary-color: #409eff;
  --hchart-text-color: #606266;
  --hchart-menu-background: #ffffff;
  --hchart-menu-border: #dcdfe6;
  --hchart-menu-shadow: rgba(0, 0, 0, 0.15);
  --hchart-menu-color: #303133;
  --hchart-menu-hover: #f0f0f0;
}

项目结构

src/
├─ core/       # HChart、BasePlugin、工具类、Cursor 联动
├─ plugins/    # Cursor、缩放、边频、阈值线、标注、右键菜单
├─ types/      # 公共 TypeScript 类型与宿主契约
├─ vue/        # Vue 3 适配组件,不承载分析逻辑
├─ react/      # React 18/19 适配组件,不承载分析逻辑
├─ index.ts    # 框架无关入口
├─ vue.ts      # Vue 3 入口
└─ react.ts    # React 入口

playground/    # Vue 3 交互示例
examples/      # Vue 3 与 React 使用示例
tests/         # 核心逻辑单元测试
docs/          # 架构说明与发布清单

本地开发

npm install
npm run typecheck
npm run test
npm run build
npm run pack:check

playground/ 提供 Vue 3 交互示例,playground-react/ 提供 React 交互示例(可通过 npm run dev:react 启动)。两者均展示对同一核心 API 的使用;Vue playground 还包含双向 Cursor 联动、频谱分析插件组合、动态 Series 数据更新、右键菜单和 PNG 导出示例。

开源与发布计划

  • 发布 npm 公共包
  • 提供 Vue 3 在线示例和 API 文档
  • 增加 Web Components 等其他宿主适配层
  • 建立版本变更记录与自动化发布流程

待完善

  • 完善频谱、时域、包络分析测试
  • 增加工业监测主题、暗色大屏主题和国际化示例

相关文档

  • 贡献指南
  • 安全策略
  • 变更日志

License

本项目基于 MIT License 开源。

Related Packages

@syidax/advanced-pro

Interactive charts and data grids for React, Angular, Vue and the web. Pro visua...

262
@syidax/advanced

Interactive charts and data grids for React, Angular, Vue and the web. Community...

255
nuxt-echarts

Nuxt Module for Apache ECharts™

816,976
vuejs-highcharts

Wrapper for Highcharts library for Vue.js 2.x

5,970 1
@putianyi888/vue3-plots

Composable Vue 3 SVG plot layers for building charts from small, focused compone...

8,240 0

Version History

Version Released Vue License
1.0.0 ^3.3.0 MIT
0.1.0 ^3.3.0 MIT