@damoqiongqiu/ice-chart 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +530 -0
- package/dist/index.cjs +25 -0
- package/dist/index.mjs +25 -0
- package/dist/index.umd.js +25 -0
- package/dist/types/ICEChart.d.mts +330 -0
- package/dist/types/ICEChart.d.ts +330 -0
- package/dist/types/a11y.d.mts +85 -0
- package/dist/types/a11y.d.ts +85 -0
- package/dist/types/animation/motion.d.mts +11 -0
- package/dist/types/animation/motion.d.ts +11 -0
- package/dist/types/components/Axis.d.mts +60 -0
- package/dist/types/components/Axis.d.ts +60 -0
- package/dist/types/components/Brush.d.mts +19 -0
- package/dist/types/components/Brush.d.ts +19 -0
- package/dist/types/components/ChartComponent.d.mts +48 -0
- package/dist/types/components/ChartComponent.d.ts +48 -0
- package/dist/types/components/Crosshair.d.mts +40 -0
- package/dist/types/components/Crosshair.d.ts +40 -0
- package/dist/types/components/DataZoomSlider.d.mts +30 -0
- package/dist/types/components/DataZoomSlider.d.ts +30 -0
- package/dist/types/components/GridLines.d.mts +37 -0
- package/dist/types/components/GridLines.d.ts +37 -0
- package/dist/types/components/Highlight.d.mts +34 -0
- package/dist/types/components/Highlight.d.ts +34 -0
- package/dist/types/components/Legend.d.mts +26 -0
- package/dist/types/components/Legend.d.ts +26 -0
- package/dist/types/components/PlotArea.d.mts +21 -0
- package/dist/types/components/PlotArea.d.ts +21 -0
- package/dist/types/components/PolarGrid.d.mts +34 -0
- package/dist/types/components/PolarGrid.d.ts +34 -0
- package/dist/types/components/RadarGrid.d.mts +25 -0
- package/dist/types/components/RadarGrid.d.ts +25 -0
- package/dist/types/components/Title.d.mts +14 -0
- package/dist/types/components/Title.d.ts +14 -0
- package/dist/types/components/Tooltip.d.mts +48 -0
- package/dist/types/components/Tooltip.d.ts +48 -0
- package/dist/types/components/series/AreaSeries.d.mts +14 -0
- package/dist/types/components/series/AreaSeries.d.ts +14 -0
- package/dist/types/components/series/BarSeries.d.mts +39 -0
- package/dist/types/components/series/BarSeries.d.ts +39 -0
- package/dist/types/components/series/BoxplotSeries.d.mts +16 -0
- package/dist/types/components/series/BoxplotSeries.d.ts +16 -0
- package/dist/types/components/series/CandlestickSeries.d.mts +13 -0
- package/dist/types/components/series/CandlestickSeries.d.ts +13 -0
- package/dist/types/components/series/CurveSeriesBase.d.mts +58 -0
- package/dist/types/components/series/CurveSeriesBase.d.ts +58 -0
- package/dist/types/components/series/FunctionSeries.d.mts +22 -0
- package/dist/types/components/series/FunctionSeries.d.ts +22 -0
- package/dist/types/components/series/FunnelSeries.d.mts +38 -0
- package/dist/types/components/series/FunnelSeries.d.ts +38 -0
- package/dist/types/components/series/GaugeSeries.d.mts +45 -0
- package/dist/types/components/series/GaugeSeries.d.ts +45 -0
- package/dist/types/components/series/GraphSeries.d.mts +39 -0
- package/dist/types/components/series/GraphSeries.d.ts +39 -0
- package/dist/types/components/series/HeatmapSeries.d.mts +21 -0
- package/dist/types/components/series/HeatmapSeries.d.ts +21 -0
- package/dist/types/components/series/LineSeries.d.mts +31 -0
- package/dist/types/components/series/LineSeries.d.ts +31 -0
- package/dist/types/components/series/LiquidSeries.d.mts +55 -0
- package/dist/types/components/series/LiquidSeries.d.ts +55 -0
- package/dist/types/components/series/ParametricSeries.d.mts +30 -0
- package/dist/types/components/series/ParametricSeries.d.ts +30 -0
- package/dist/types/components/series/PieSeries.d.mts +44 -0
- package/dist/types/components/series/PieSeries.d.ts +44 -0
- package/dist/types/components/series/RadarSeries.d.mts +30 -0
- package/dist/types/components/series/RadarSeries.d.ts +30 -0
- package/dist/types/components/series/SankeySeries.d.mts +34 -0
- package/dist/types/components/series/SankeySeries.d.ts +34 -0
- package/dist/types/components/series/ScatterSeries.d.mts +11 -0
- package/dist/types/components/series/ScatterSeries.d.ts +11 -0
- package/dist/types/components/series/SeriesBase.d.mts +214 -0
- package/dist/types/components/series/SeriesBase.d.ts +214 -0
- package/dist/types/components/series/TreemapSeries.d.mts +48 -0
- package/dist/types/components/series/TreemapSeries.d.ts +48 -0
- package/dist/types/components/series/WaterfallSeries.d.mts +17 -0
- package/dist/types/components/series/WaterfallSeries.d.ts +17 -0
- package/dist/types/components/series/createSeries.d.mts +10 -0
- package/dist/types/components/series/createSeries.d.ts +10 -0
- package/dist/types/components/series/index.d.mts +29 -0
- package/dist/types/components/series/index.d.ts +29 -0
- package/dist/types/expr/diagnostics.d.mts +45 -0
- package/dist/types/expr/diagnostics.d.ts +45 -0
- package/dist/types/expr/expr.d.mts +44 -0
- package/dist/types/expr/expr.d.ts +44 -0
- package/dist/types/expr/sample.d.mts +34 -0
- package/dist/types/expr/sample.d.ts +34 -0
- package/dist/types/index.d.mts +80 -0
- package/dist/types/index.d.ts +80 -0
- package/dist/types/interaction/ChartLink.d.mts +16 -0
- package/dist/types/interaction/ChartLink.d.ts +16 -0
- package/dist/types/interaction/HitResolver.d.mts +44 -0
- package/dist/types/interaction/HitResolver.d.ts +44 -0
- package/dist/types/interaction/InteractionController.d.mts +147 -0
- package/dist/types/interaction/InteractionController.d.ts +147 -0
- package/dist/types/internal.d.mts +232 -0
- package/dist/types/internal.d.ts +232 -0
- package/dist/types/layout/force.d.mts +48 -0
- package/dist/types/layout/force.d.ts +48 -0
- package/dist/types/layout/layout.d.mts +9 -0
- package/dist/types/layout/layout.d.ts +9 -0
- package/dist/types/layout/sankey.d.mts +47 -0
- package/dist/types/layout/sankey.d.ts +47 -0
- package/dist/types/layout/treemap.d.mts +35 -0
- package/dist/types/layout/treemap.d.ts +35 -0
- package/dist/types/option/normalize.d.mts +68 -0
- package/dist/types/option/normalize.d.ts +68 -0
- package/dist/types/scale/BandScale.d.mts +36 -0
- package/dist/types/scale/BandScale.d.ts +36 -0
- package/dist/types/scale/LinearScale.d.mts +21 -0
- package/dist/types/scale/LinearScale.d.ts +21 -0
- package/dist/types/scale/LogScale.d.mts +24 -0
- package/dist/types/scale/LogScale.d.ts +24 -0
- package/dist/types/scale/Scale.d.mts +38 -0
- package/dist/types/scale/Scale.d.ts +38 -0
- package/dist/types/scale/TimeScale.d.mts +28 -0
- package/dist/types/scale/TimeScale.d.ts +28 -0
- package/dist/types/scale/format.d.mts +5 -0
- package/dist/types/scale/format.d.ts +5 -0
- package/dist/types/scale/index.d.mts +7 -0
- package/dist/types/scale/index.d.ts +7 -0
- package/dist/types/theme/chartTheme.d.mts +54 -0
- package/dist/types/theme/chartTheme.d.ts +54 -0
- package/dist/types/types.d.mts +689 -0
- package/dist/types/types.d.ts +689 -0
- package/dist/types/util/emitter.d.mts +11 -0
- package/dist/types/util/emitter.d.ts +11 -0
- package/dist/types/util/math.d.mts +14 -0
- package/dist/types/util/math.d.ts +14 -0
- package/dist/types/util/text.d.mts +10 -0
- package/dist/types/util/text.d.ts +10 -0
- package/package.json +100 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2026 大漠穷秋
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
# ICEChart · 交互式图表库
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@damoqiongqiu/ice-chart)
|
|
4
|
+
[](https://www.npmjs.com/package/@damoqiongqiu/ice-chart)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](https://github.com/ice-render/ice-chart/actions/workflows/ci.yml)
|
|
7
|
+
|
|
8
|
+
构建在 [ice-render](https://github.com/ice-render/ice-render) Canvas 引擎之上的**交互式图表库**。
|
|
9
|
+
|
|
10
|
+
它不是「把数据画成图」的又一个图表库 —— 命中测试、事件派发、嵌套坐标系、脏矩形局部重绘
|
|
11
|
+
全部交给 ice-render 引擎,图表层只负责把「数据 ↔ 像素 ↔ 语义事件」这三件事打通。
|
|
12
|
+
于是悬停、点击下钻、框选、缩放平移、图例联动、跨图联动、键盘导航都是**内建能力**,
|
|
13
|
+
而不是事后打补丁的插件。
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
## 快速开始
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @damoqiongqiu/ice-chart ice-render
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> 包名带作用域不是偏好,是 npm 的「相似名保护」:无作用域的 `ice-chart` 与已存在的 `icechart`
|
|
24
|
+
> 太像,会被直接拒发,所以本包发布在 `@damoqiongqiu` 作用域下。
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { createChart } from '@damoqiongqiu/ice-chart';
|
|
28
|
+
|
|
29
|
+
const chart = createChart('canvas-id', {
|
|
30
|
+
title: { text: '近 30 天流量' },
|
|
31
|
+
tooltip: { trigger: 'axis' },
|
|
32
|
+
interaction: {
|
|
33
|
+
hover: { enabled: true, dimOthers: true }, // 悬停高亮 + 压暗其他系列
|
|
34
|
+
select: { enabled: true, mode: 'multiple' }, // 点多选(或键盘 Enter)
|
|
35
|
+
brush: { enabled: true, axes: 'x', mode: 'zoom' }, // 拖拽框选,直接当缩放用
|
|
36
|
+
zoom: { enabled: true, axes: 'x', wheel: true },
|
|
37
|
+
pan: { enabled: true, axes: 'x' },
|
|
38
|
+
keyboard: true,
|
|
39
|
+
},
|
|
40
|
+
xAxis: { type: 'category' },
|
|
41
|
+
yAxis: { name: '访问量' },
|
|
42
|
+
series: [
|
|
43
|
+
{ id: 'pv', type: 'line', name: '访问量', data: [820, 932, 901, 1290] },
|
|
44
|
+
{ id: 'uv', type: 'area', name: '独立访客', data: [320, 402, 391, 520] },
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
chart.on('item:click', (params) => {
|
|
49
|
+
console.log(params.seriesName, params.xValue, params.value, params.data);
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`ice-render` 是 peer 依赖:一个页面上多张图共用同一个引擎实例池,跨图联动才有统一的事件语义。
|
|
54
|
+
包同时提供 ESM / CJS / UMD 三种产物与完整类型声明(`dist/types`),
|
|
55
|
+
Vite / webpack / Rollup 直接 import,Node 侧 `require('@damoqiongqiu/ice-chart')` 也拿得到 CJS。
|
|
56
|
+
|
|
57
|
+
浏览器直接用(CDN 或本地文件,注意引擎要先于图表引入):
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<canvas id="chart" width="960" height="420"></canvas>
|
|
61
|
+
<script src="https://unpkg.com/ice-render/dist/index.umd.js"></script>
|
|
62
|
+
<script src="https://unpkg.com/@damoqiongqiu/ice-chart/dist/index.umd.js"></script>
|
|
63
|
+
<script>
|
|
64
|
+
ICEChart.createChart('chart', { series: [{ type: 'line', data: [1, 3, 2] }] });
|
|
65
|
+
</script>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 设计原则
|
|
69
|
+
|
|
70
|
+
**0. 视觉基调是 Bootstrap**
|
|
71
|
+
|
|
72
|
+
默认主题直接取 Bootstrap 5 的调色板与设计变量(primary / success / danger / warning / info、
|
|
73
|
+
gray-100~900、`--bs-border-radius`、`--bs-body-font-family`),图表放进 Bootstrap 页面里
|
|
74
|
+
与按钮、卡片、表格是同一套视觉语言。需要换品牌色时用 `theme: { colorPalette: [...] }` 覆盖即可,
|
|
75
|
+
或直接改 `BOOTSTRAP_TOKENS` 派生自己的主题。
|
|
76
|
+
|
|
77
|
+

|
|
78
|
+
|
|
79
|
+
**1. 交互是一等公民,命中判定写进组件**
|
|
80
|
+
|
|
81
|
+
每个系列组件都实现 `containsLocalPoint`:把组件本地坐标翻译成数据语义(离折线多近、落在哪根柱子里),
|
|
82
|
+
于是引擎的 `ice.hitTest()` / 事件派发**天然就认识数据点**,不需要在图表外面再写一套坐标反查。
|
|
83
|
+
代价是 0 —— 这套判定本来就写在同一个类里,还顺带复用了「画出来是什么样」的像素缓存。
|
|
84
|
+
|
|
85
|
+
**2. 像素缓存是渲染与命中的唯一事实来源**
|
|
86
|
+
|
|
87
|
+
`rebuildPixels()` 在数据 / 比例尺 / 尺寸变化时重算一次点集,`render()` 与 `hitTestIndex()` 消费同一份缓存。
|
|
88
|
+
「看得见的点」与「点得到的点」因此不可能漂移。
|
|
89
|
+
|
|
90
|
+
**3. 交互的视觉反馈是独立小组件**
|
|
91
|
+
|
|
92
|
+
鼠标在数据点上移动时,只有 `Highlight` / `Crosshair` / `Tooltip` 这三个覆盖层变脏,
|
|
93
|
+
脏矩形就是标记环那一小块像素 —— 折线与柱形完全不动。压暗其他系列只在「悬停系列变了」时
|
|
94
|
+
写一次 state,不会每帧把所有系列置脏。
|
|
95
|
+
|
|
96
|
+
下图是同一个页面上同时打开悬停 / 框选 / dataZoom 的样子:框选区域是独立覆盖层,
|
|
97
|
+
折线本身没有重绘,底部是语义事件日志。
|
|
98
|
+
|
|
99
|
+

|
|
100
|
+
|
|
101
|
+
**4. 声明式 spec 可序列化**
|
|
102
|
+
|
|
103
|
+
`ChartOption` 是纯 JSON(函数字段仅限 formatter),`chart.toJSON()` / `fromJSONString()` 可存盘、可进 DSL。
|
|
104
|
+
归一化(`normalizeOption`)与布局(`computeLayout`)都是**纯函数**,不依赖 DOM / ctx,可被完整单测。
|
|
105
|
+
|
|
106
|
+
**5. 跨图联动按数据值而不是像素**
|
|
107
|
+
|
|
108
|
+
`linkCharts()` 只用公开语义事件,并通过 `fractionOf / valueAtFraction` 这类**数据域比例**对齐,
|
|
109
|
+
不同尺寸、不同数据范围的图表也能联动。
|
|
110
|
+
|
|
111
|
+
## 能力清单
|
|
112
|
+
|
|
113
|
+
| 能力 | 状态 | 说明 |
|
|
114
|
+
| --- | --- | --- |
|
|
115
|
+
| 系列类型 | line / area / bar(含横向)/ scatter(含气泡)/ pie(含环形、玫瑰)/ radar / candlestick / heatmap / sankey / funnel / gauge / boxplot / waterfall / treemap / graph / function / parametric | 见下方「图表类型与写法」 |
|
|
116
|
+
| 比例尺 | linear / category / time / log | time 轴按跨度自动切换毫秒~年粒度 |
|
|
117
|
+
| 坐标系 | 直角坐标 / 极坐标(饼图) / 雷达 / 桑基图 | 按系列类型自动切换场景 |
|
|
118
|
+
| 坐标轴 | x + **多 y 轴**(左右可配) | 刻度、网格、轴名、标签旋转与自动抽稀、自定义 formatter |
|
|
119
|
+
| 图例 | top / bottom / left / right | **可点击切换系列 / 扇区显隐**并重算数据域 |
|
|
120
|
+
| 提示框 | axis / item 触发器 | 画在画布内(小程序同样可用);K 线给 OHLC、桑基给流量 |
|
|
121
|
+
| 十字准星 | x / y / xy | 带坐标轴数值标签 |
|
|
122
|
+
| 悬停高亮 | 圆环 / 柱形描边 | 可配置 `dimOthers` 压暗其他系列 |
|
|
123
|
+
| 选中 | single / multiple | 点击或键盘 Enter,抛出 `select:change` |
|
|
124
|
+
| 框选 | x / y / xy,select / zoom 两种模式 | 拖拽出选区,实时抛 `brush:change` |
|
|
125
|
+
| 缩放 | 滚轮(data / viewport 两种模式) | 以指针位置为锚点,可配置 `minSpan / maxSpan` |
|
|
126
|
+
| 平移 | 拖拽 | 自动约束在完整数据域内 |
|
|
127
|
+
| 键盘导航 | ←/→ 移动数据点,↑/↓ 切换系列 | Enter 选中,Esc 清空;只由最后激活的图表响应 |
|
|
128
|
+
| 跨图联动 | hover / zoom / brush | `linkCharts([a, b])`,按 x 数据值对齐 |
|
|
129
|
+
| 动画 | 进入与数据更新 | 走引擎的 `AnimationManager`(`state.progress` 驱动) |
|
|
130
|
+
| 主题 | light / dark / 自定义片段 | 默认色板取自 ice-render 的设计 token |
|
|
131
|
+
| 大数据 | LTTB 降采样 + 二分命中 | 5 万点 × 3 系列构建 35ms,每条曲线只绘制约 2 点/像素 |
|
|
132
|
+
| 无障碍 | 数据表镜像 + aria-live 播报 | `attachA11yMirror()` / `getDataTable()` / `getA11yTree()` |
|
|
133
|
+
| 序列化 | `toJSON` / `fromJSONString` | 配置 + 缩放窗口 + 图例显隐状态 |
|
|
134
|
+
|
|
135
|
+
## 事件
|
|
136
|
+
|
|
137
|
+
所有事件都通过 `chart.on(name, handler)` 订阅:
|
|
138
|
+
|
|
139
|
+
| 事件 | 载荷 | 触发时机 |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| `item:hover` | `DataPointParams` | 悬停数据点 / 数据列(axis 触发器取该列第一个点) |
|
|
142
|
+
| `item:leave` | — | 离开数据 |
|
|
143
|
+
| `item:click` | `DataPointParams` | 点击数据点(下钻的入口) |
|
|
144
|
+
| `item:dblclick` | `DataPointParams` | 双击数据点 |
|
|
145
|
+
| `plot:click` | `{ screen, xValue, yValue }` | 点击绘图区空白处 |
|
|
146
|
+
| `chart:click` | `{ screen }` | 点击画布(绘图区之外) |
|
|
147
|
+
| `select:change` | `DataPointParams[]` | 选中集合变化 |
|
|
148
|
+
| `brush:change` | `BrushRange \| null` | 框选拖动中(实时) |
|
|
149
|
+
| `brush:end` | `BrushRange \| null` | 框选结束 |
|
|
150
|
+
| `zoom:change` | `ZoomRange` | 缩放 / 框选缩放的窗口变化 |
|
|
151
|
+
| `pan:change` | `ZoomRange` | 拖拽平移 |
|
|
152
|
+
| `legend:toggle` | `LegendToggleParams` | 图例切换系列 |
|
|
153
|
+
|
|
154
|
+
`DataPointParams` 同时携带 `dataIndex / xValue / value / data`(原始数据项)与 `screen` 像素坐标,
|
|
155
|
+
业务层做下钻、联动、埋点都不需要再碰比例尺。
|
|
156
|
+
|
|
157
|
+
## 图表类型与写法
|
|
158
|
+
|
|
159
|
+
每种类型都是「声明式 option + 相同的交互语义」,切换类型只需要改 `series[].type`。
|
|
160
|
+
|
|
161
|
+
| 类型 | 关键写法 | 说明 |
|
|
162
|
+
| --- | --- | --- |
|
|
163
|
+
| `line` / `area` | `data: [1, 2, 3]` 或 `[[x, y]]` | 平滑曲线 `smooth`、断点(`null` 断开)、面积 `areaOpacity` |
|
|
164
|
+
| `bar` | 类目在 x(默认) | 分组(多系列)与堆叠(同 `stack` 名);数据项写 `{ value, color }` 可**逐项配色** |
|
|
165
|
+
| `bar`(横向) | `yAxis: { type: 'category', data: [...] }` + `xAxis: { type: 'value' }` | 排行榜;类目也可写在数据项的 `name` 上 |
|
|
166
|
+
| `scatter` | `data: [[x, y, size]]` + `symbolSizeRange` | 第三维映射成直径即气泡图;`symbolSize` 也可传函数 |
|
|
167
|
+
| `pie` | `data: [{ name, value }]` | `innerRadius` 出环形,`roseType` 出玫瑰图;扇区可点图例隐藏 |
|
|
168
|
+
| `radar` | `radar.indicators` + `data: [数值...]` | 一个系列一个多边形,顶点命中 |
|
|
169
|
+
| `candlestick` | `data: [[open, close, low, high]]` | 影线进数据域,提示框给 OHLC |
|
|
170
|
+
| `boxplot` | `data: [[min, Q1, median, Q3, max]]` 或一组原始观测值 | 恰好 5 个数按五数概括解释,其它长度自动算分位数;命中覆盖整条须 |
|
|
171
|
+
| `heatmap` | `data: [[x类目, y类目, 数值]]` | y 轴自动变类目轴,颜色线性插值 |
|
|
172
|
+
| `waterfall` | `data: [{ name, value }]`,合计项标 `total: true` | 增/减/合计三色 + 连接虚线 |
|
|
173
|
+
| `funnel` | `data: [{ name, value }]` | 阶段梯形、`minSize` 保护最小阶段、图例按阶段显隐 |
|
|
174
|
+
| `gauge` | `gauge: { min, max, axisLineColor }` + `data: [{ name, value }]` | 指针随数值转动,轴线按阈值分段配色 |
|
|
175
|
+
| `sankey` | `sankey: { nodes, links }` | 分层 + 纵向松弛布局,节点/连线分别命中 |
|
|
176
|
+
| `treemap` | `data: [{ name, value, children }]` | squarified 布局,父节点留标题带;命中返回最深节点 |
|
|
177
|
+
| `graph` | `graph: { nodes, links }` | 力导向布局(无底图),节点可拖拽重排;按分类配色、按权重定大小 |
|
|
178
|
+
| `function` | `expression: 'sin(x)/x'`(+ `params` / `domain` / `samples` / `adaptive`) | 迷你 MATLAB:直接写表达式画 `y = f(x)`,按可视区间重采样、y 轴自动贴合;默认**自适应细分**,`adaptive: false` 才是均匀采样 |
|
|
179
|
+
| `parametric` | `xExpression: 'sin(3*t)'` + `yExpression: 'cos(2*t)'` | 参数曲线(李萨如 / 螺线 / 心形线);自变量是 `t` |
|
|
180
|
+
| `parametric`(极坐标) | `polarExpression: 'cos(3*t)'` + `polarGrid: true` | 极坐标 `r(θ)`(玫瑰线 / 心形线 / 螺线),配 `aspect: 'equal'` 出 MATLAB `polarplot` 观感 |
|
|
181
|
+
| `liquid` | `liquid: { min, max }` + `data: [{ name, value }]` | 水位球(数据大屏常客):水位随数值升降、水面持续起伏;整球可命中 |
|
|
182
|
+
|
|
183
|
+
### 实时数据流
|
|
184
|
+
|
|
185
|
+
数据不断进来、图形跟着动(监控大屏 / 交易终端那类)走 `appendData`:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
chart.appendData('cpu', [[t, value]], { maxPoints: 180 }); // 追加 + 滑动窗口
|
|
189
|
+
chart.appendData('cpu', [[t, v1], [t2, v2]], { maxPoints: 180, animate: true }); // 需要值插值时才开
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
- **滑动窗口**:追加到末尾,超过 `maxPoints` 从头裁掉;x 轴窗口自动跟着右移,不需要手动 `setDomain`;
|
|
193
|
+
- **默认不做值插值**:窗口滑动会让下标整体前移,插值会把每个点拖向「邻居的值」(看起来像被拖住)。
|
|
194
|
+
流畅度由推送频率决定——60Hz 推送就是 60fps 的平滑滚动;
|
|
195
|
+
- 走常规更新路径 + `preserveView`(当前缩放窗口不受影响),并派发 `data:change`;
|
|
196
|
+
- 实测(2 系列、每 tick 各追加 1 点):窗口 120 点 **1.6ms/tick**、600 点 4.9ms、1500 点 11ms
|
|
197
|
+
—— 监控场景用 120~300 点的窗口最划算。
|
|
198
|
+
|
|
199
|
+
配套示例 [examples/live-stream.html](./examples/live-stream.html):四条曲线共用一个数据发生器 ——
|
|
200
|
+
滑动窗口折线 / 弹簧指针仪表盘 / 每 250ms 左移一列的滚动热力图 / 最后一根实时跳动的 K 线,
|
|
201
|
+
外加暂停、1×/2×/4× 速度、注入尖峰与 fps 统计。
|
|
202
|
+
|
|
203
|
+
### 大屏(深色主题)
|
|
204
|
+
|
|
205
|
+
同一个脚手架([examples/assets/dash-kit.js](./examples/assets/dash-kit.js))下**六个大屏案例**,
|
|
206
|
+
每个只是「换一套视觉身份 + 换一张面板清单」:
|
|
207
|
+
|
|
208
|
+
| 大屏 | 视觉身份 | 侧重 |
|
|
209
|
+
| --- | --- | --- |
|
|
210
|
+
| [运营监控](./examples/dashboard.html) | 青 | 12 张图共用一条数据流,跨图三路联动 + 告警亮边 |
|
|
211
|
+
| [设备监控](./examples/dashboard-iot.html) | 青绿 | 水位球 / 设备状态热力 / 心跳 K 线 / 固件占比 |
|
|
212
|
+
| [行情监控](./examples/dashboard-market.html) | 琥珀 + 红涨绿跌 | 分时 / 盘口六档 / 资金流桑基 / 换手水位球 |
|
|
213
|
+
| [能源调度](./examples/dashboard-energy.html) | 蓝紫 | 源网荷桑基 / 电量平衡瀑布 / **谐波合成(函数绘图)** |
|
|
214
|
+
| [物流调度](./examples/dashboard-logistics.html) | 橙红 | 分拣漏斗 / 包裹流向 / 逐项配色的排名与时段柱 |
|
|
215
|
+
| [函数实验](./examples/dashboard-lab.html) | 紫 | 参数扫动 / 极坐标 / 采样密度 / 表达式诊断 |
|
|
216
|
+
|
|
217
|
+
`dash-kit` 只做三件事:注入共享 CSS(面板 / KPI 条 / 顶栏 / 底部快照面板)、
|
|
218
|
+
按 12 列栅格产出面板 HTML、跑主循环(`tick` + 暂停 + 倍速 + 供审计用的 `__dashLoop`)。
|
|
219
|
+
页面本身只写「配色变量 + 面板清单 + 数据怎么动」——新增一个大屏的量级是**一个 HTML 文件**。
|
|
220
|
+
|
|
221
|
+
以 [运营监控大屏](./examples/dashboard.html) 为例:
|
|
222
|
+
|
|
223
|
+
- 深色主题(`theme: 'dark'`)+ 自绘大屏外壳(KPI 卡片 / 面板标题栏 / 告警亮边);
|
|
224
|
+
- 折线与延迟用 `appendData` 滑动窗口(60Hz),并用 `linkCharts` 做**悬停 / 缩放 / 框选三路联动**;
|
|
225
|
+
- **水位球**(`type: 'liquid'`)随 CPU 升降、水面持续起伏 —— 大屏里最有辨识度的一张;
|
|
226
|
+
- 仪表盘(弹簧指针)、雷达(实时抖动)、热力图(每 250ms 左移一列)按不同频率 `setData`;
|
|
227
|
+
- 玫瑰图 / 矩形树图 / 漏斗走更新动画做**重排过渡**(值变化时图形是滑过去的,不是瞬跳);
|
|
228
|
+
- 桑基图开 `flow`,链路方向用流动虚线表达;
|
|
229
|
+
- CPU > 85% 时面板亮红边、顶部告警点亮起 —— 点「注入尖峰」看整屏反应;
|
|
230
|
+
- 底部是序列化 JSON 面板(12 张图切换查看各自的真实快照)。
|
|
231
|
+
|
|
232
|
+
> 外观按大屏的通用视觉基调重做过一轮(近黑蓝底 + 单一强调色 + 亮角面板 + KPI 分隔条),
|
|
233
|
+
> 布局用**严格 12 列栅格**(12 × 120px + 12px 间距 = 1572px 设计宽):面板是列宽的整数倍,
|
|
234
|
+
> 画布宽 = 面板宽 − 内边距 − 边框,所以所有面板的左右边缘与内部留白完全对齐。
|
|
235
|
+
> 12 张图(含水位球)同时流动实测 41~60fps。
|
|
236
|
+
> 每个大屏的**画布左右留白都是 9/9**、所有面板左边缘都落在 132px 栅格上(脚本量测,不是目测)。
|
|
237
|
+
|
|
238
|
+
两个只在大屏里用得上的能力:
|
|
239
|
+
|
|
240
|
+
- **逐项配色**:数据项写成 `{ value, color }`,一个系列就能表达分级
|
|
241
|
+
(承运商准时率、时段是否越限、机组出力档位),不必拆成多个系列把图形排成一组一组;
|
|
242
|
+
横向排行榜按值排序后重建类目轴,「排名第一」永远在最上面。
|
|
243
|
+
- **`adaptive: false`**:函数系列默认按曲率**自适应细分**采样(所以只把 `samples` 调小看不出粗糙),
|
|
244
|
+
显式关掉才是真正的均匀采样 —— 函数实验大屏里「9 点 / 40 点 / 自适应」三线同屏对照。
|
|
245
|
+
|
|
246
|
+
## 函数绘图(迷你 MATLAB)
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
ICEChart.createChart('chart', {
|
|
250
|
+
aspect: 'equal', // 等比坐标:一个数据单位等长(画圆 / 参数曲线必开)
|
|
251
|
+
xAxis: { type: 'value' },
|
|
252
|
+
yAxis: {},
|
|
253
|
+
series: [
|
|
254
|
+
{ type: 'function', name: 'sin(x)/x', expression: 'sin(x)/x', domain: [-10, 10] },
|
|
255
|
+
{ type: 'function', name: 'a·sin(x)·e^-|x|/6', expression: 'a*sin(x)*exp(-abs(x)/6)', params: { a: 1.5 } },
|
|
256
|
+
{ type: 'parametric', name: '李萨如', xExpression: 'sin(3*t)', yExpression: 'cos(2*t)', domain: [0, Math.PI * 2] },
|
|
257
|
+
],
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
表达式引擎是自研的(`src/expr/`,**不用 `eval` / `new Function`**,CSP 安全),
|
|
262
|
+
支持 `+ - * / % ^`、`sin/cos/tan/exp/log/sqrt/abs/min/max/clamp/...`、常量 `pi/e/tau`,
|
|
263
|
+
以及 MATLAB 习惯的**隐式乘法**(`2x`、`3sin(x)`、`2(x+1)`)。写错了会带上位置指针报错,
|
|
264
|
+
但**不会把图表搞崩**:`chart.expressionErrors()` 把原因交给表单去标红。
|
|
265
|
+
|
|
266
|
+
配套示例 [examples/mini-matlab.html](./examples/mini-matlab.html) 可以在页面上直接改公式、
|
|
267
|
+
拖参数、切换参数曲线与极坐标曲线:
|
|
268
|
+
|
|
269
|
+

|
|
270
|
+
|
|
271
|
+
### 怎么知道用户写错了公式
|
|
272
|
+
|
|
273
|
+
`chart.expressionDiagnostics()` 把三层检查合成一份结果(`error` 画不出来 / `warning` 多半不是本意):
|
|
274
|
+
|
|
275
|
+
| 层 | code | 例子 | 级别 |
|
|
276
|
+
| --- | --- | --- | --- |
|
|
277
|
+
| 语法 | `syntax` / `unknown-character` / `unknown-function` / `arity` / `empty` | `sin(x`、`foo(x)`、`sin(1,2)` | error(带字符位置) |
|
|
278
|
+
| 静态 | `unknown-variable` | `b*sin(x)` 但没定义 `b` | error |
|
|
279
|
+
| 静态 | `unused-parameter` | `params: {a: 1}` 但表达式里没有 `a` | warning |
|
|
280
|
+
| 运行 | `no-finite-values` | 整段 `sqrt(-1-x^2)`、`log(0*x-1)` | error |
|
|
281
|
+
| 运行 | `constant-value` | `sin(0)`,画出来是一条水平线 | warning |
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
for (const { seriesId, diagnostics } of chart.expressionDiagnostics()) {
|
|
285
|
+
for (const d of diagnostics) {
|
|
286
|
+
// d.severity: 'error' | 'warning';d.code 见上表;d.position 只有语法类才有
|
|
287
|
+
markInputRed(seriesId, d.message + (d.position === undefined ? '' : `(位置 ${d.position})`));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
chart.expressionErrors(); // 只取 error(标红用)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
诊断**永远不让图表崩**(一个手滑的输入不该把整张图搞没);有静态错误时不再跑运行层检查,
|
|
294
|
+
避免「`b` 没定义」连带报一条「整段画不出来」这种症状级联。
|
|
295
|
+
|
|
296
|
+
几个刻意的设计:
|
|
297
|
+
|
|
298
|
+
- **按可视区间采样**:缩放之后按新的 x 区间重新采样并在曲率大的地方自适应加点,
|
|
299
|
+
所以放大看局部会越来越细,而不是把稀疏折线拉大;
|
|
300
|
+
- **y 轴自动贴合**:数据域取可视区间内的**稳健范围**(IQR 剪掉离群尖峰),
|
|
301
|
+
`1/x`、`tan(x)` 不会把 y 轴拉到 ±2500;显式写了 `yAxis.min/max` 或缩放过 y 就以它为准;
|
|
302
|
+
- **极点是真断点**:`tan(x)` 的渐近线两侧不会连出一条竖直假线(采样阶段就写成 NaN 分段);
|
|
303
|
+
- **参数扫动动画**:`sweep: { name: 'a', from: -3, to: 3 }` 让曲线连续变形 ——
|
|
304
|
+
表达式每帧重新求值(实测 3 条曲线同屏 58fps),这是引擎持续重绘能力最自然的用法。
|
|
305
|
+
- **等比坐标**:`aspect: 'equal'`(MATLAB 的 `axis equal`)让 x / y 一个数据单位在屏幕上等长,
|
|
306
|
+
绘图区同时收缩成正方形。参数曲线 / 圆 / 几何图形不开它会被拉成椭圆 ——
|
|
307
|
+
实测单位圆在不等比时 x 方向 438px/单位、y 方向 153px/单位(拉伸 2.86 倍)。
|
|
308
|
+
- **极坐标 r(θ)**:`polarExpression` 写 r 的公式(自变量 θ 用 `t` 表示),组件按
|
|
309
|
+
`x = r·cosθ, y = r·sinθ` 展开成同一条参数曲线;配 `aspect: 'equal'` + `polarGrid: true`
|
|
310
|
+
就是 MATLAB 的 `polarplot`(同心圆 + 辐条底图,半径刻度沿 45° 方向):
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
{ aspect: 'equal', polarGrid: { splitNumber: 4, spokeCount: 12 },
|
|
314
|
+
series: [{ type: 'parametric', polarExpression: 'cos(3*t)', // 三瓣玫瑰线
|
|
315
|
+
domain: [0, Math.PI * 2], name: 'r = cos(3θ)' }] }
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
网格圆心取**比例尺映射后的原点**、半径取「原点到最近边界」——所以同心圆永远完整落在绘图区内,
|
|
319
|
+
数据域不对称也不会跑偏。
|
|
320
|
+
|
|
321
|
+
## 动画
|
|
322
|
+
|
|
323
|
+
默认播**入场动画**(首次渲染就会播,不是只有更新才播)。`animation` 分三段,每段可单独配置或用 `false` 关掉:
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
animation: {
|
|
327
|
+
enter: { duration: 900, easing: 'easeOutCubic', stagger: 0.45 }, // 首次渲染 / 新增系列
|
|
328
|
+
update: { duration: 700, easing: 'easeOutCubic' }, // setData / setOption
|
|
329
|
+
highlight: { duration: 260, easing: 'springSnappy' }, // 悬停反馈
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
- **错峰 `stagger`**:把入场拆成波浪(队列靠前的数据项先动),所有项仍在同一时刻结束。
|
|
334
|
+
- **缓动**直接用引擎的曲线名:`linear`、`easeIn*/easeOut*/easeInOut*`(Quad / Cubic / Quart)以及三条**解析弹簧**
|
|
335
|
+
`spring` / `springSoft` / `springSnappy` —— 仪表盘指针、气泡弹出、交互反馈用它们最自然。
|
|
336
|
+
- 兼容扁平写法:`animation: { duration, easing, stagger }` 等价于配置 `enter`。
|
|
337
|
+
- **动效偏好**(无障碍):`ICEChart.setMotionPreference('instant')` 让所有动画瞬时到位;
|
|
338
|
+
`'auto'`(默认)跟随系统的 `prefers-reduced-motion`;`'full'` 始终播动画。
|
|
339
|
+
`chart.finishAnimations()` 可把当前动画一次性推到终态(截图 / 测试用)。
|
|
340
|
+
|
|
341
|
+
各类型的入场形态:柱形从基线错峰长出、折线/面积从左到右画出来、气泡依次弹出、
|
|
342
|
+
饼图/玫瑰图扇形依次扫开、雷达从中心展开、K 线从开盘价上下展开、箱线图从中位线展开、
|
|
343
|
+
热力图沿对角线逐格浮现、漏斗从等宽收拢成漏斗、仪表盘指针扫到目标值(可配弹簧回弹)、
|
|
344
|
+
桑基连线从源流向目标、矩形树图逐层展开、关系图从环形铺开**收敛到力布局结果**。
|
|
345
|
+
|
|
346
|
+
数据更新时,系列的值会从旧值插值到新值,**坐标轴数据域也跟着一起过渡**(否则域瞬跳会让图形先蹦一下再动)。
|
|
347
|
+
|
|
348
|
+
### 交互过程中的动画
|
|
349
|
+
|
|
350
|
+
鼠标交互不是「瞬间换一张图」,所有反馈都走引擎补间,因此中途也保持连贯:
|
|
351
|
+
|
|
352
|
+
- **悬停放大**:被悬停的图元自己沿语义方向做微放大 —— 柱子从基线往外伸长(不是整体平移)、
|
|
353
|
+
气泡变大、饼图/玫瑰图扇形沿中角向外「脱出」。`SeriesBase.setHoverIndex()` 由交互层统一下发,
|
|
354
|
+
移开后同一个补间反向回落。柱形的绘制矩形可用 `barDrawRectAt(i)` 取到(测试断言用)。
|
|
355
|
+
- **提示框淡入淡出**:出现时淡入 + 上滑 6px;消失时淡出到 0 才清内容,不会「淡入很柔、消失很硬」。
|
|
356
|
+
淡出途中重新悬停会从当前透明度继续淡入(`Tooltip.panelOpacity()` 可断言)。
|
|
357
|
+
- **准星平滑跟随**:换列时从当前位置补间到新列,跟上「快速划过」的手感;
|
|
358
|
+
`Crosshair.pixelX/pixelY` 始终是目标值,绘制位置在 `state.axisX/axisY`。
|
|
359
|
+
- **框选蚂蚁线**:拖框期间虚线相位持续推进,松手即停(不会留下一直在重绘的组件)。
|
|
360
|
+
- **桑基流动**:`sankey: { flow: true, flowSpeed: 40 }` 给连线加一层沿路径流动的白色虚线,
|
|
361
|
+
表达方向与速率;不需要时保持关闭以免每帧重绘。
|
|
362
|
+
- **矩形树图形变**:树图数据更新时,矩形在**旧布局 → 新布局**之间插值(而不是瞬间跳布局),
|
|
363
|
+
子节点按相对父矩形的比例跟随父矩形一起缩放,中途不会露出空隙或错位。
|
|
364
|
+
- **坐标轴刻度过渡**:缩放 / 平移 / 数据更新时,刻度从旧位置滑到新位置,新出现的淡入、消失的淡出;
|
|
365
|
+
网格线与刻度走同一条时间线(不会出现「标签在滑、网格线在跳」)。
|
|
366
|
+
连续滚轮缩放时从当前渲染位置接着走,不会每次都从旧位置重跳。
|
|
367
|
+
- **图例切换重排**:隐藏一个饼图扇区 / 漏斗阶段时,其余几何平滑挪位、被隐藏的那个收拢再消失;
|
|
368
|
+
切换系列显隐时数值域与其它系列一起过渡。
|
|
369
|
+
|
|
370
|
+
## 主要 API
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
const chart = createChart(canvasOrId, option, { renderMode: 'dirty-rect', dpr: 2, autoResize: true });
|
|
374
|
+
|
|
375
|
+
chart.setOption(nextOption); // 保留当前缩放窗口
|
|
376
|
+
chart.setData('series-id', nextData); // 只更新一个系列的数据(原地更新,不重建组件)
|
|
377
|
+
chart.setDomain('x', [100, 300]); // 设置数据域(缩放 / 联动)
|
|
378
|
+
chart.resetZoom(); // 恢复完整数据域
|
|
379
|
+
chart.toggleSeries('series-id', true); // 显隐系列
|
|
380
|
+
chart.showHoverAt('series-id', 12); // 程序化高亮某个数据点
|
|
381
|
+
chart.showHoverAtValue(xValue); // 按 x 数据值高亮(跨图联动入口)
|
|
382
|
+
chart.resize(960, 420); // 手动重排
|
|
383
|
+
chart.toJSON() / fromJSONString(json); // 序列化
|
|
384
|
+
await chart.render(); // 等待下一帧渲染完成(截图 / 测试用)
|
|
385
|
+
chart.destroy();
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
## 架构
|
|
389
|
+
|
|
390
|
+
### 序列化:持久化的单位是 option 快照,不是组件树
|
|
391
|
+
|
|
392
|
+
- `chart.toJSON()` 产出 `{ version, option, view, hidden, hiddenSlices }`:
|
|
393
|
+
option 是声明式规格(**唯一事实来源**),view 是缩放窗口,hidden / hiddenSlices 是图例与扇区显隐。
|
|
394
|
+
- `chart.fromJSONObject(snapshot)` / `fromJSONString(json)` 原地重建;
|
|
395
|
+
`ICEChart.restore(canvas, snapshot)`、或 `createChart(canvas, snapshot)`(识别到快照自动走还原)
|
|
396
|
+
用于在新画布上重建。
|
|
397
|
+
- 往返**无损且幂等**:还原后再导出,JSON 与原文逐字节一致;浏览器里两张画布
|
|
398
|
+
`toDataURL()` 也完全一致(`examples/serialize.html` 现场做这个比对)。
|
|
399
|
+
- 函数字段(`formatter` 等)进不了 JSON,导出时被丢弃,还原时用 `optionPatch` 补回来:
|
|
400
|
+
|
|
401
|
+
```ts
|
|
402
|
+
const chart = ICEChart.restore('canvas-2', json, {
|
|
403
|
+
optionPatch: {
|
|
404
|
+
tooltip: { formatter: (p) => `${p.xValue} → ${p.items[0].value}` },
|
|
405
|
+
series: [{ id: 'visits', label: { formatter: (p) => `${p.name} ${p.percent}%` } }],
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
每个示例页底部都挂着这块面板,直接显示**当前图表的真实快照**(多图页面按图切换 tab)。
|
|
411
|
+
它不是调试用的字符串,而是 `toJSON()` 的原样输出 —— 缩放、平移、图例切换之后会自动刷新:
|
|
412
|
+
|
|
413
|
+

|
|
414
|
+
|
|
415
|
+
**为什么不用引擎的组件树?** `ice.toJSONString()` 确实能存下组件树(几何 + 样式),
|
|
416
|
+
但组件树是 option 的**渲染投影**:没有比例尺、数据点、命中缓存这些语义,
|
|
417
|
+
反序列化回来只是一棵空壳(未注册类型会被整段跳过)。所以 ice-chart 刻意让
|
|
418
|
+
「规格 → 组件树」保持单向编译,持久化只认规格。
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
const json = chart.toJSONString(); // 导出:纯数据,KB 级别
|
|
422
|
+
const restored = createChart('canvas-2', json); // 还原:语义 / 窗口 / 显隐全部一致
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
```
|
|
426
|
+
ChartOption(纯 JSON)
|
|
427
|
+
│ normalizeOption() 纯函数:数据点 / 数据域 / 堆叠
|
|
428
|
+
▼
|
|
429
|
+
NormalizedOption
|
|
430
|
+
│ computeLayout() 纯函数:标题 / 图例 / 坐标轴 / 绘图区
|
|
431
|
+
▼
|
|
432
|
+
ChartLayout
|
|
433
|
+
│ ICEChart 编译成 ice-render 组件树
|
|
434
|
+
▼
|
|
435
|
+
┌──────────────────────────────────────────────────────┐
|
|
436
|
+
│ ICEGroup(root) │
|
|
437
|
+
│ ├ PlotArea 绘图区背景 + 空白处交互面 │
|
|
438
|
+
│ ├ GridLines 网格线 │
|
|
439
|
+
│ ├ LineSeries / BarSeries / PieSeries / RadarSeries / CandlestickSeries / ... ← containsLocalPoint 即数据命中判定
|
|
440
|
+
│ ├ Axis × N 坐标轴(多 y 轴) │
|
|
441
|
+
│ ├ RadarGrid 雷达网格(仅雷达场景) │
|
|
442
|
+
│ ├ Title / Legend │
|
|
443
|
+
│ ├ Crosshair / Highlight / Brush / Tooltip 覆盖层 │
|
|
444
|
+
│ └ DataZoomSlider 缩放滑块 │
|
|
445
|
+
└──────────────────────────────────────────────────────┘
|
|
446
|
+
│ ice.hitTest() → 组件 → 数据下标
|
|
447
|
+
▼
|
|
448
|
+
InteractionController → 语义事件(item:hover / brush:end / ...)
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## 示例
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
npm run build && npm run examples:prepare
|
|
455
|
+
npm run examples:serve # http://localhost:5177
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
示例页面覆盖:基础折线 / 面积、分组与堆叠柱形、多 y 轴叠加、饼图 / 环形图 / 玫瑰图、雷达图、
|
|
459
|
+
K 线与热力图、桑基图、交互总览(框选 + 多选 + 键盘 + 事件日志)、时间轴 + dataZoom 滑块、
|
|
460
|
+
大数据量(5 万点降采样)、无障碍、跨图联动、迷你 MATLAB,以及 **6 个深色大屏**
|
|
461
|
+
(运营 / 设备 / 行情 / 能源 / 物流 / 函数实验)。
|
|
462
|
+
|
|
463
|
+
六个大屏共用同一套脚手架(`examples/assets/dash-kit.js`:12 列栅格 + 面板组件 + 数据流主循环),
|
|
464
|
+
每个大屏只换一套配色身份与面板清单 —— 底色与主色同源,是「同一个库、不同视觉身份」的六种样子:
|
|
465
|
+
|
|
466
|
+

|
|
467
|
+
|
|
468
|
+
每个示例页在图表下方都有两块面板:
|
|
469
|
+
|
|
470
|
+
- **序列化 JSON**(`examples/assets/snapshot-panel.js`):实时显示 `chart.toJSON()` 的**真实内容**,
|
|
471
|
+
带语法高亮、版本 / 体积 / 系列数 / 更新时间,以及 刷新 / 复制 / 下载 按钮。
|
|
472
|
+
缩放、平移、图例切换后自动刷新(150ms 防抖)。多图页面提供图表切换 tab;
|
|
473
|
+
超大快照(如 5 万点的 3.7 MB)只美化显示截断后的预览,复制 / 下载仍是完整内容。
|
|
474
|
+
- **事件日志**:把 `item:hover` / `brush:end` / `zoom:change` 等语义事件打出来。
|
|
475
|
+
|
|
476
|
+
## 开发
|
|
477
|
+
|
|
478
|
+
引擎按 **npm 依赖**装(`peerDependencies` + `devDependencies` 都是 `ice-render@^1.4.7`),
|
|
479
|
+
`npm install` 即可跑测试和示例。要连着改引擎源码时,把 `devDependencies` 那条临时改成
|
|
480
|
+
`file:../ice-render`(引擎仓库放同级目录)再 `npm install`。
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
npm test # jest(纯函数单测 + 真实引擎集成的 jsdom 测试)
|
|
484
|
+
npm run types:check # tsc --noEmit
|
|
485
|
+
npm run build # ESM + CJS + UMD + .d.ts/.d.mts
|
|
486
|
+
npm run verify # lint → types:check → build → test
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
交互外观审计(需要浏览器):
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
npm run build && npm run examples:prepare
|
|
493
|
+
node scripts/serve-examples.cjs &
|
|
494
|
+
npm run audit:interactions -- ./.audit # 27 页 × 11 步交互,逐步截图 + 几何断言
|
|
495
|
+
npm run audit:hover -- ./.hover-sweep # 18 种图表逐个数据点悬停:反馈动画 + 像素缓存新鲜度
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
审计会检查每一步之后:提示框是否越出画布、是否压住坐标轴数值标签或图例、
|
|
499
|
+
高亮标记是否落在绘图区内、有没有饱和色墨迹跑到坐标轴带上;任何一条不满足就以非 0 退出码结束,可用于 CI。
|
|
500
|
+
(图例带例外:图例色块本来就是饱和色、又画在绘图区外面,居中的图例落在等比坐标的轴带里不算越界。)
|
|
501
|
+
|
|
502
|
+
悬停实测(`audit:hover`)会把指针移到每个数据点上,逐点断言三件事:
|
|
503
|
+
交互层把 `hoverIndex` 下发到了对应系列、反馈动画确实推进到 1、悬停几何没有越界;
|
|
504
|
+
同时做一次**像素缓存新鲜度**检查(清掉缓存键重算,两次像素必须一致)——
|
|
505
|
+
它抓的是「缩放 / 数据变化后 `pixels` 没重算,悬停高亮画在别处」这类缓存 bug。
|
|
506
|
+
|
|
507
|
+
README 里的截图也是脚本拍的(同一个浏览器环境、同一条示例服务):
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
node scripts/readme-shots.mjs # 重新生成 docs/screenshots/*.png
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
测试用例覆盖的关键路径:比例尺换算、数据归一化与堆叠、布局量测、系列命中判定,
|
|
514
|
+
以及「引擎命中测试 → 数据下标 → 语义事件」这条端到端链路(含多图隔离与联动回归)。
|
|
515
|
+
|
|
516
|
+
## 路线图
|
|
517
|
+
|
|
518
|
+
已落地:极坐标(饼图 / 玫瑰图)、雷达图、多 y 轴、dataZoom 滑块、LTTB 降采样与二分命中、
|
|
519
|
+
无障碍(数据表镜像 + 播报)、K 线、热力图、桑基图。
|
|
520
|
+
|
|
521
|
+
后续候选:
|
|
522
|
+
|
|
523
|
+
- 桑基节点拖拽重排与折叠(布局已与渲染解耦,扩展成本低)
|
|
524
|
+
- 数据 append 的增量绘制(当前是全量重建像素缓存)
|
|
525
|
+
- y 轴方向的 dataZoom 滑块(`setAxisDomain` 已可用,缺 UI)
|
|
526
|
+
- 地图 / 力导向关系图(属于另一类布局族)
|
|
527
|
+
|
|
528
|
+
## License
|
|
529
|
+
|
|
530
|
+
MIT
|