transone-chart 0.1.2

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.
Files changed (46) hide show
  1. package/README.md +181 -0
  2. package/dist/adapters/index.d.ts +7 -0
  3. package/dist/adapters/miniprogram.d.ts +44 -0
  4. package/dist/adapters/native.d.ts +28 -0
  5. package/dist/adapters/types.d.ts +27 -0
  6. package/dist/adapters/web.d.ts +13 -0
  7. package/dist/charts/bar.d.ts +25 -0
  8. package/dist/charts/index.d.ts +4 -0
  9. package/dist/charts/line.d.ts +20 -0
  10. package/dist/charts/pie.d.ts +14 -0
  11. package/dist/charts/radar.d.ts +16 -0
  12. package/dist/component.d.ts +41 -0
  13. package/dist/core/axis.d.ts +47 -0
  14. package/dist/core/canvas.d.ts +61 -0
  15. package/dist/core/chart.d.ts +69 -0
  16. package/dist/core/layout.d.ts +58 -0
  17. package/dist/core/legend.d.ts +26 -0
  18. package/dist/core/scale.d.ts +67 -0
  19. package/dist/core/text.d.ts +20 -0
  20. package/dist/factory.d.ts +22 -0
  21. package/dist/index.d.ts +29 -0
  22. package/dist/index.js +17 -0
  23. package/dist/index.js.map +49 -0
  24. package/dist/types.d.ts +180 -0
  25. package/lib/adapters/index.ts +18 -0
  26. package/lib/adapters/miniprogram.ts +146 -0
  27. package/lib/adapters/native.ts +39 -0
  28. package/lib/adapters/types.ts +44 -0
  29. package/lib/adapters/web.ts +41 -0
  30. package/lib/charts/bar.ts +270 -0
  31. package/lib/charts/index.ts +4 -0
  32. package/lib/charts/line.ts +153 -0
  33. package/lib/charts/pie.ts +110 -0
  34. package/lib/charts/radar.ts +166 -0
  35. package/lib/component.ts +149 -0
  36. package/lib/core/axis.ts +174 -0
  37. package/lib/core/canvas.ts +130 -0
  38. package/lib/core/chart.ts +322 -0
  39. package/lib/core/layout.ts +221 -0
  40. package/lib/core/legend.ts +94 -0
  41. package/lib/core/scale.ts +183 -0
  42. package/lib/core/text.ts +70 -0
  43. package/lib/factory.ts +62 -0
  44. package/lib/index.ts +69 -0
  45. package/lib/types.ts +218 -0
  46. package/package.json +55 -0
package/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # transone-chart
2
+
3
+ TransOne 生态的跨端图表库:**一份 TypeScript 源码,基于 Canvas 2D 渲染**,同时跑通 Web 与微信 / 阿里 / 字节小程序,并为未来原生 App(iOS / Android / 鸿蒙)预留扩展契约。
4
+
5
+ 一期内置四种常用图表:**折线图 / 柱状图 / 饼图(环形)/ 雷达图**。
6
+
7
+ - 零运行时依赖(`transone` 仅组件集成为 optional peer)
8
+ - OOP + 策略模式:`core(纯引擎)→ charts(图表策略)→ adapters(平台差异)→ factory(分发)→ component(声明式组件)`
9
+ - 跨端唯一解耦边界是 `ICanvas2D` 契约(图表所需的最小 Canvas 2D 子集)
10
+ - 与 `transone` 组件系统无缝集成:`<TcChart option={...} />` 一份代码编译到 Web / 小程序
11
+
12
+ ## 安装
13
+
14
+ ```bash
15
+ bun add transone-chart # 运行时(peer: transone >= 0.3.0)
16
+ bun add -d transone-chart # 仅引擎用法时
17
+ ```
18
+
19
+ ## 快速开始
20
+
21
+ ### 方式一:声明式组件(推荐,配合 transone)
22
+
23
+ ```tsx
24
+ import { Component, createComponent } from 'transone';
25
+ import { TcChart } from 'transone-chart';
26
+
27
+ // 任意图表 Option
28
+ const option = {
29
+ type: 'line',
30
+ title: { text: '城市指数趋势' },
31
+ xAxis: { labels: ['4月', '5月', '6月', '7月', '8月', '9月'] },
32
+ series: [
33
+ { name: '北京', data: [82, 85, 84, 88, 90, 92], smooth: true },
34
+ { name: '上海', data: [80, 83, 86, 85, 89, 91], smooth: true, area: true },
35
+ ],
36
+ };
37
+
38
+ class MyPage extends Component {
39
+ render() {
40
+ return createComponent({ component: TcChart, props: { option } });
41
+ }
42
+ }
43
+ ```
44
+
45
+ `TcChart` 是**受控组件**:`option` 由父级传入,数据变化自动 `setOption + render`;组件卸载自动销毁。
46
+
47
+ ### 方式二:引擎直用(无框架场景)
48
+
49
+ ```ts
50
+ import { createWebChart, createMiniProgramChart } from 'transone-chart';
51
+
52
+ // Web:canvas 元素
53
+ const chart = createWebChart(canvasElement, option);
54
+ chart.render();
55
+
56
+ // 小程序:Canvas 2D 节点
57
+ const chart2 = createMiniProgramChart(canvasNode, option);
58
+ chart2.render();
59
+
60
+ // 高级:自定义渲染上下文(如原生桥层实现 ICanvas2D 后)
61
+ import { createChart } from 'transone-chart';
62
+ const chart3 = createChart(context, option); // context 为 ChartRenderContext
63
+ chart3.render();
64
+ ```
65
+
66
+ ## 图表 API 一览
67
+
68
+ 所有配置均为 `ECharts` 心智(`title / legend / xAxis / yAxis / series`),但零依赖纯 Canvas 绘制。
69
+
70
+ | 图表 | Option 类型 | 核心配置 |
71
+ |---|---|---|
72
+ | 折线 | `LineChartOption` | `smooth` 平滑曲线、`area` 面积填充、`showSymbol` 数据点、`startFromZero` |
73
+ | 柱状 | `BarChartOption` | `stack` 堆叠分组、`horizontal` 横向、`borderRadius` 圆角、`barWidth` |
74
+ | 饼图 | `PieChartOption` | `radius / innerRadius`(环形)、`startAngle`、扇区百分比标签 |
75
+ | 雷达 | `RadarChartOption` | `indicators[].max` 归一化、`splitCount` 网格层、`area` 多边形填充 |
76
+
77
+ 完整字段见 [`lib/types.ts`](./lib/types.ts)(每个字段均带中文注释)。
78
+
79
+ ## 跨端支持
80
+
81
+ | 平台 | 支持 | 说明 |
82
+ |---|---|---|
83
+ | Web | ✅ | `HTMLCanvasElement.getContext('2d')` 直接满足 `ICanvas2D` |
84
+ | 微信小程序 | ✅ | `<canvas type="2d">` + SelectorQuery 节点;CLI 已内置 canvas 标签映射 |
85
+ | 阿里小程序 | ✅ | 同一适配层(安全取全局 `my` / `tt` / `wx`) |
86
+ | 字节小程序 | ✅ | 同上 |
87
+ | iOS / Android / 鸿蒙 App | 🔜 契约就绪 | 实现 `ICanvas2D`(约 20 个方法)即可接入,见下节 |
88
+
89
+ > 小程序端 DPR 通过各端 `getSystemInfoSync()` 获取,像素比自动适配,无需手工处理。
90
+
91
+ ### 小程序端约束(写组件代码前必读)
92
+
93
+ transone-cli 对小程序端模板/组件有静态编译约束,`TcChart` 已按其适配,使用方注意:
94
+
95
+ - **option 请放在 `initState()` 的返回对象中**(经 data 序列化 + `wx:for` 数据绑定传给组件),
96
+ 不要直接在 `render()` 里引用模块级常量对象——静态常量会被折叠为字符串属性,对象 props 会丢失。
97
+ 参见 `playground/chart-demo/src/pages/home.ts` 的 `charts` 数组写法。
98
+ - `initState()` 内只支持 `const` 声明与 `return`(小程序端需静态求值);`render()` 内不支持调用
99
+ 自定义辅助方法(请用 `each(...)` 展开),`each` 的 key 仅支持 `(item) => item.xxx` 或 `(item, index) => index`。
100
+ - 小程序端 props 无法传函数(如 `option.format`),需要函数时请在端内判断分支。
101
+
102
+ ## 原生 App 扩展指南
103
+
104
+ 图表引擎与平台完全解耦,唯一的平台边界是 `ICanvas2D`(`lib/core/canvas.ts`)。
105
+ 原生端只需实现该契约(`beginPath / moveTo / lineTo / bezierCurveTo / arc / fill / stroke / measureText / createLinearGradient` 等),
106
+ 再通过 `adapters/native.ts` 的 `resolveNativeCanvas` 注入即可,图表层零改动。
107
+
108
+ ```ts
109
+ import { createChart } from 'transone-chart';
110
+ import { resolveNativeCanvas } from 'transone-chart/adapters';
111
+
112
+ // 原生桥层:把原生画布上下文包装成 ICanvas2D,并实现 NativeCanvasHost
113
+ const host = {
114
+ width: 375,
115
+ height: 260,
116
+ pixelRatio: 2,
117
+ getContext(type: '2d') {
118
+ return { beginPath() {}, moveTo() {}, /* ...实现 ICanvas2D */ };
119
+ },
120
+ };
121
+ const chart = createChart(resolveNativeCanvas(host), option);
122
+ chart.render();
123
+ ```
124
+
125
+ > `adapters/native.ts` 当前是契约占位:未实现时调用会显式抛错,**绝不静默降级**,
126
+ > 保证原生端不会出现"看起来渲染了其实空白"的隐性故障。
127
+
128
+ ## 架构
129
+
130
+ ```
131
+ ┌─────────────────────────────────────────────────────┐
132
+ │ component.ts TcChart 声明式组件(受控,自动生命周期) │
133
+ ├─────────────────────────────────────────────────────┤
134
+ │ factory.ts createChart(option) 按 type 分发策略 │
135
+ ├─────────────────────────────────────────────────────┤
136
+ │ charts/ LineChart / BarChart / PieChart / │
137
+ │ RadarChart(策略类,只依赖 core) │
138
+ ├─────────────────────────────────────────────────────┤
139
+ │ core/ canvas.ts(ICanvas2D 契约) · scale · │
140
+ │ layout · axis · legend · chart(基类) │
141
+ ├─────────────────────────────────────────────────────┤
142
+ │ adapters/ web.ts · miniprogram.ts · native.ts │
143
+ └─────────────────────────────────────────────────────┘
144
+ ```
145
+
146
+ 渲染管线(`ChartBase`):
147
+
148
+ ```
149
+ save → scale(dpr) → clear → 背景 → 布局(title/legend/轴区逐层扣除)
150
+ → 标题 → 图例 → 坐标轴(仅笛卡尔类) → drawSeries → restore
151
+ ```
152
+
153
+ - `seriesColor` 按索引取 `DEFAULT_PALETTE` 色板,显式 `color` 优先
154
+ - 数值轴自动 nice 刻度;类目轴无标签不预留轴区
155
+ - 图例:series 带 `name` 且未显式配置时,默认 `top` 显示
156
+
157
+ ## 开发
158
+
159
+ ```bash
160
+ bun test # 46 个单测(mock canvas 断言绘制命令)
161
+ bun run build # tsc 声明 + Bun.build(minify, ESM)
162
+ bun run --cwd ../../playground/chart-demo build:web # 演示项目构建
163
+ ```
164
+
165
+ ## 目录结构
166
+
167
+ ```
168
+ packages/transone-chart/
169
+ ├── lib/ # 源码(core / charts / adapters / factory / component / types)
170
+ ├── tests/ # 单测(scale / layout / line / bar / pie / radar / factory)
171
+ ├── scripts/build.ts # 构建脚本
172
+ └── playground 演示:playground/chart-demo(城市指数场景,五种图表卡片)
173
+ ```
174
+
175
+ ## 路线图
176
+
177
+ - [x] 一期:折线 / 柱状 / 饼 / 雷达 + Web / 小程序 + 原生契约
178
+ - [ ] 交互:tooltip / 高亮 / 点击事件(event 层)
179
+ - [ ] 更多图表:散点 / 面积 / 漏斗 / 仪表盘
180
+ - [ ] 原生 App 适配器实现(iOS / Android / 鸿蒙桥层)
181
+ - [ ] 主题系统 / 动画过渡
@@ -0,0 +1,7 @@
1
+ export type { ResolveCanvasOptions, ResolvedCanvas, } from './types';
2
+ export { detectPixelRatio } from './types';
3
+ export { resolveWebCanvas } from './web';
4
+ export { getMiniProgramCanvasNode, getMiniProgramGlobal, resolveMiniProgramCanvas, detectMiniProgramPixelRatio, } from './miniprogram';
5
+ export type { MiniProgramCanvasNode, MiniProgramGlobal } from './miniprogram';
6
+ export { resolveNativeCanvas } from './native';
7
+ export type { NativeCanvasHost } from './native';
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 小程序适配器:微信 / 阿里 / 字节的 Canvas 2D 节点 → ChartRenderContext。
3
+ *
4
+ * 获取流程(各端一致,仅全局名不同):
5
+ * canvas type="2d" → 节点 node → node.getContext('2d') → ICanvas2D
6
+ *
7
+ * 节点获取依赖 SelectorQuery,属于组件生命周期职责,由
8
+ * getMiniProgramCanvasNode() 提供;本文件只负责「节点 → 上下文」,
9
+ * 以及 DPR 探测(pixelRatio 从各端系统信息接口读取)。
10
+ */
11
+ import type { ChartRenderContext } from '../types';
12
+ import { type ResolveCanvasOptions } from './types';
13
+ /** 小程序 Canvas 2D 节点(各端 node 的公共形状)。 */
14
+ export interface MiniProgramCanvasNode {
15
+ getContext(type: '2d'): unknown;
16
+ width: number;
17
+ height: number;
18
+ }
19
+ export type MiniProgramGlobal = 'wx' | 'my' | 'tt';
20
+ /** 安全读取小程序全局(避免与官方类型定义冲突,模式同 transone request)。 */
21
+ export declare function getMiniProgramGlobal(name: MiniProgramGlobal): Record<string, unknown> | null;
22
+ /**
23
+ * 通过 SelectorQuery 获取 Canvas 2D 节点(组件/页面内调用)。
24
+ * 各端 API 形状一致:createSelectorQuery().in(instance).select(selector)
25
+ * .fields({ node: true, size: true }).exec(callback)
26
+ */
27
+ export declare function getMiniProgramCanvasNode(options: {
28
+ /** 端标识,缺省自动探测(wx → my → tt)。 */
29
+ platform?: MiniProgramGlobal;
30
+ /** 组件/页面实例(.in() 入参)。 */
31
+ instance?: unknown;
32
+ /** canvas 选择器,如 '#chart'。 */
33
+ selector: string;
34
+ }): Promise<MiniProgramCanvasNode>;
35
+ /** 探测当前小程序全局(wx → my → tt),均不存在返回 null。 */
36
+ export declare function detectMiniProgramGlobal(): MiniProgramGlobal | null;
37
+ /** 读取各端设备像素比(wx.getSystemInfoSync / my.getSystemInfoSync / tt.getSystemInfoSync)。 */
38
+ export declare function detectMiniProgramPixelRatio(platform?: MiniProgramGlobal): number;
39
+ /**
40
+ * 从 Canvas 2D 节点解析渲染上下文。
41
+ * 物理画布尺寸由节点自身管理(width/height 已在平台侧按像素比设置),
42
+ * 此处只做归一化返回。
43
+ */
44
+ export declare function resolveMiniProgramCanvas(node: MiniProgramCanvasNode, options?: ResolveCanvasOptions): ChartRenderContext;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 原生 App 适配器(占位 / 契约声明)。
3
+ *
4
+ * 未来 iOS / Android / 鸿蒙 原生端接入路径:
5
+ * 1. 宿主提供原生 canvas 能力(Skia / ArkUI Canvas / WebView 桥),
6
+ * 把原生绘制 API 实现为 ICanvas2D(见 core/canvas.ts 契约)
7
+ * 2. 实现 resolveNativeCanvas(),返回 ChartRenderContext(含 DPR 与逻辑尺寸)
8
+ * 3. 之后引擎与四种图表零改动运行
9
+ *
10
+ * 本文件当前不导出可运行实现,仅保留类型契约与接入文档,防止
11
+ * 平台差异悄悄渗入引擎。
12
+ */
13
+ import type { ChartRenderContext } from '../types';
14
+ /** 未来原生端需实现的最小宿主接口。 */
15
+ export interface NativeCanvasHost {
16
+ /** 逻辑尺寸(CSS px)。 */
17
+ width: number;
18
+ height: number;
19
+ /** 设备像素比。 */
20
+ pixelRatio: number;
21
+ /** 原生绘图上下文(宿主实现为 ICanvas2D 契约)。 */
22
+ getContext(type: '2d'): unknown;
23
+ }
24
+ /**
25
+ * 未来实现:原生 canvas 节点 → ChartRenderContext。
26
+ * 当前未接入任何原生端,调用会明确报错,避免静默降级。
27
+ */
28
+ export declare function resolveNativeCanvas(_host: NativeCanvasHost): ChartRenderContext;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 平台适配层:把各端 Canvas 2D 上下文解析为统一渲染上下文
3
+ * (ICanvas2D + 逻辑尺寸 + DPR)。
4
+ *
5
+ * 适配器只负责三件事:
6
+ * 1. 拿到上下文(HTMLCanvasElement / 小程序 canvas node / 未来原生 canvas)
7
+ * 2. 计算设备像素比并设置物理画布尺寸
8
+ * 3. 返回引擎需要的 ChartRenderContext
9
+ *
10
+ * 引擎代码永远不直接接触 DOM / wx / native API。
11
+ */
12
+ import type { ChartRenderContext } from '../types';
13
+ export interface ResolveCanvasOptions {
14
+ /** 逻辑宽度(CSS px);缺省取节点实际尺寸。 */
15
+ width?: number;
16
+ /** 逻辑高度(CSS px);缺省取节点实际尺寸。 */
17
+ height?: number;
18
+ /** 设备像素比;缺省自动探测。 */
19
+ dpr?: number;
20
+ /** 自定义色板。 */
21
+ palette?: readonly string[];
22
+ }
23
+ export interface ResolvedCanvas {
24
+ context: ChartRenderContext;
25
+ }
26
+ /** 通用探测设备像素比(web 与小程序可用 globalThis 访问)。 */
27
+ export declare function detectPixelRatio(): number;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Web 适配器:HTMLCanvasElement → ChartRenderContext。
3
+ *
4
+ * - 把物理画布尺寸设为 逻辑尺寸 × DPR,保证高分屏清晰
5
+ * - 上下文为标准 CanvasRenderingContext2D,天然满足 ICanvas2D 契约
6
+ */
7
+ import type { ChartRenderContext } from '../types';
8
+ import { type ResolveCanvasOptions } from './types';
9
+ /**
10
+ * 从 HTMLCanvasElement 解析渲染上下文。
11
+ * 会就地修改 canvas.width / canvas.height 为物理像素尺寸。
12
+ */
13
+ export declare function resolveWebCanvas(canvas: HTMLCanvasElement, options?: ResolveCanvasOptions): ChartRenderContext;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 柱状图:纵向 / 横向,支持多系列分组与 stack 堆叠。
3
+ *
4
+ * 几何规则:
5
+ * - 分组:同一类目内多个系列并排,子柱宽 = 类目带宽 × (1 - gap) / 系列数 × 0.8
6
+ * - 堆叠:同名 stack 系列共用一根柱,后一个系列从前一个的顶端继续累加
7
+ * - 数值轴强制包含 0(柱形高度必须从零线起算才有意义)
8
+ */
9
+ import type { ICanvas2D } from '../core/canvas';
10
+ import { ChartBase } from '../core/chart';
11
+ import type { LayoutResult } from '../core/layout';
12
+ import type { LegendItem } from '../core/legend';
13
+ import type { BarChartOption } from '../types';
14
+ export declare class BarChart extends ChartBase<BarChartOption> {
15
+ protected isHorizontal(): boolean;
16
+ protected getValueDomain(): {
17
+ min: number;
18
+ max: number;
19
+ };
20
+ protected getCategoryLabels(): readonly string[];
21
+ protected getLegendItems(): LegendItem[];
22
+ protected drawSeries(ctx: ICanvas2D, layout: LayoutResult, option: BarChartOption): void;
23
+ private computeSlot;
24
+ private drawBar;
25
+ }
@@ -0,0 +1,4 @@
1
+ export { LineChart } from './line';
2
+ export { BarChart } from './bar';
3
+ export { PieChart } from './pie';
4
+ export { RadarChart } from './radar';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 折线图:多系列 / 平滑曲线 / 面积填充 / 数据点标记。
3
+ * 类目 x 轴 + 数值 y 轴,复用 ChartBase 的笛卡尔管线。
4
+ */
5
+ import type { ICanvas2D } from '../core/canvas';
6
+ import { ChartBase } from '../core/chart';
7
+ import type { LayoutResult } from '../core/layout';
8
+ import type { LegendItem } from '../core/legend';
9
+ import type { LineChartOption } from '../types';
10
+ export declare class LineChart extends ChartBase<LineChartOption> {
11
+ protected getValueDomain(): {
12
+ min: number;
13
+ max: number;
14
+ };
15
+ protected getCategoryLabels(): readonly string[];
16
+ protected getLegendItems(): LegendItem[];
17
+ protected drawSeries(ctx: ICanvas2D, layout: LayoutResult, option: LineChartOption): void;
18
+ /** 折线路径:直线或 Catmull-Rom 平滑插值(三次贝塞尔)。 */
19
+ private tracePath;
20
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 饼图 / 环形图:扇区绘制、百分比标签、图例。
3
+ * 无笛卡尔坐标轴,全部几何在绘图区内按极坐标计算。
4
+ */
5
+ import type { ICanvas2D } from '../core/canvas';
6
+ import { ChartBase } from '../core/chart';
7
+ import type { LayoutResult } from '../core/layout';
8
+ import type { LegendItem } from '../core/legend';
9
+ import type { PieChartOption } from '../types';
10
+ export declare class PieChart extends ChartBase<PieChartOption> {
11
+ protected hasCartesianAxis(): boolean;
12
+ protected getLegendItems(): LegendItem[];
13
+ protected drawSeries(ctx: ICanvas2D, layout: LayoutResult, option: PieChartOption): void;
14
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 雷达图:多边形网格 + 指标轴 + 多系列区域/折线。
3
+ * 指标刻度按各 indicator.max(缺省取所有系列该指标最大值)归一化。
4
+ */
5
+ import type { ICanvas2D } from '../core/canvas';
6
+ import { ChartBase } from '../core/chart';
7
+ import type { LayoutResult } from '../core/layout';
8
+ import type { LegendItem } from '../core/legend';
9
+ import type { RadarChartOption } from '../types';
10
+ export declare class RadarChart extends ChartBase<RadarChartOption> {
11
+ protected hasCartesianAxis(): boolean;
12
+ protected getLegendItems(): LegendItem[];
13
+ protected drawSeries(ctx: ICanvas2D, layout: LayoutResult, option: RadarChartOption): void;
14
+ private indicatorMax;
15
+ private tracePolygon;
16
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 可选集成:基于 transone Component 的声明式图表组件 TcChart。
3
+ *
4
+ * 与 transone 组件系统集成,一份源码同时编译 Web 与小程序:
5
+ * - Web:canvas 元素由组件渲染,onMounted 后自动解析并渲染
6
+ * - 小程序:canvas 编译为 <canvas type="2d">,通过 SelectorQuery 获取节点
7
+ * - 未来原生:通过 resolve 注入自定义解析器
8
+ *
9
+ * 组件本身是薄壳:状态由父级以 option 传入(完全受控),
10
+ * 数据变化时自动 setOption + render。
11
+ */
12
+ import { Component, type VNode } from 'transone';
13
+ import type { ChartRenderContext, ChartOption } from './types';
14
+ import type { ChartBase } from './core/chart';
15
+ export interface TcChartProps {
16
+ /** 图表配置(line / bar / pie / radar)。 */
17
+ option: ChartOption;
18
+ /** 自定义解析器:把 canvas 元素/节点解析为渲染上下文。 */
19
+ resolve?: (element: unknown) => Promise<ChartRenderContext> | ChartRenderContext;
20
+ className?: string;
21
+ }
22
+ interface TcChartState {
23
+ canvasId: string;
24
+ }
25
+ export declare class TcChart extends Component<TcChartProps, TcChartState> {
26
+ private chart;
27
+ private destroyed;
28
+ protected initState(): TcChartState;
29
+ protected initStyles(): void;
30
+ protected render(): VNode;
31
+ protected onMounted(): void;
32
+ protected onUpdated(): void;
33
+ protected onUnmounted(): void;
34
+ /** 对外暴露图表实例(高级用法:手动 resize / 订阅事件)。 */
35
+ getChart(): ChartBase<ChartOption> | null;
36
+ private attachChart;
37
+ /** 组件根元素(canvas)。Web 端用于解析渲染上下文;小程序端经 SelectorQuery .in(实例) 查询,不依赖此方法。 */
38
+ getElement(): HTMLCanvasElement | null;
39
+ private resolveContext;
40
+ }
41
+ export {};
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 坐标轴绘制:数值轴(默认纵向居左、横向图居底)与类目轴(默认横向居底、
3
+ * 横向图居左)。网格线与刻度标签统一在这里绘制,数据系列只画在 plot 内。
4
+ *
5
+ * 位置计算全部基于布局结果与比例尺,与平台无关。
6
+ */
7
+ import type { ICanvas2D } from './canvas';
8
+ import type { Box } from './layout';
9
+ import { CategoryScale, LinearScale } from './scale';
10
+ export interface ValueAxisDrawOptions {
11
+ /** 绘图区。 */
12
+ plot: Box;
13
+ /** 数值比例尺:纵向图 range=[plot.bottom, plot.top],横向图 range=[plot.left, plot.right]。 */
14
+ scale: LinearScale;
15
+ /** 刻度标签(长度需与 scale.ticks 一致)。 */
16
+ labels: readonly string[];
17
+ /** 横向图:数值轴位于底部。 */
18
+ horizontal?: boolean;
19
+ axisColor?: string;
20
+ labelFontSize?: number;
21
+ labelColor?: string;
22
+ gridColor?: string;
23
+ gridLineWidth?: number;
24
+ gridLineDash?: readonly number[];
25
+ showGrid?: boolean;
26
+ }
27
+ export interface CategoryAxisDrawOptions {
28
+ plot: Box;
29
+ scale: CategoryScale;
30
+ labels: readonly string[];
31
+ /** 横向图:类目轴位于左侧。 */
32
+ horizontal?: boolean;
33
+ axisColor?: string;
34
+ labelFontSize?: number;
35
+ labelColor?: string;
36
+ gridColor?: string;
37
+ gridLineWidth?: number;
38
+ showGrid?: boolean;
39
+ }
40
+ export declare class AxisDrawer {
41
+ private readonly ctx;
42
+ constructor(ctx: ICanvas2D);
43
+ /** 绘制数值轴:网格横线(或纵向图时) + 刻度标签。 */
44
+ drawValueAxis(options: ValueAxisDrawOptions): void;
45
+ /** 绘制类目轴:轴线 + 类目标签(可选竖网格)。 */
46
+ drawCategoryAxis(options: CategoryAxisDrawOptions): void;
47
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * 跨端 Canvas 2D 契约(ICanvas2D)。
3
+ *
4
+ * 这是 transone-chart 与平台解耦的唯一边界:
5
+ * - Web:HTMLCanvasElement.getContext('2d') 天然满足,适配器薄封装
6
+ * - 小程序:微信 / 阿里 / 字节的 Canvas 2D(type="2d")节点上下文与 Web 接口基本对齐
7
+ * - 未来原生 App:Skia / ArkUI 等原生 canvas 只需实现本接口即可接入同一套引擎
8
+ *
9
+ * 接口刻意收敛为图表实际需要的最小真子集,避免依赖平台各自的扩展 API;
10
+ * 绘制一律使用「beginPath → 路径命令 → fill()/stroke() 无参」形式,
11
+ * 不依赖 Path2D 等小程序端可能缺失的高级对象。
12
+ */
13
+ export interface IGradient {
14
+ addColorStop(offset: number, color: string): void;
15
+ }
16
+ export type CanvasLineCap = 'butt' | 'round' | 'square';
17
+ export type CanvasLineJoin = 'bevel' | 'round' | 'miter';
18
+ export type CanvasTextAlign = 'left' | 'right' | 'center' | 'start' | 'end';
19
+ export type CanvasTextBaseline = 'top' | 'hanging' | 'middle' | 'alphabetic' | 'ideographic' | 'bottom';
20
+ export interface ICanvas2D {
21
+ save(): void;
22
+ restore(): void;
23
+ translate(x: number, y: number): void;
24
+ scale(x: number, y: number): void;
25
+ rotate(angle: number): void;
26
+ fillStyle: string | IGradient;
27
+ strokeStyle: string | IGradient;
28
+ lineWidth: number;
29
+ lineCap: CanvasLineCap;
30
+ lineJoin: CanvasLineJoin;
31
+ globalAlpha: number;
32
+ font: string;
33
+ textAlign: CanvasTextAlign;
34
+ textBaseline: CanvasTextBaseline;
35
+ beginPath(): void;
36
+ closePath(): void;
37
+ moveTo(x: number, y: number): void;
38
+ lineTo(x: number, y: number): void;
39
+ bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
40
+ arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
41
+ arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void;
42
+ rect(x: number, y: number, width: number, height: number): void;
43
+ ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
44
+ fill(): void;
45
+ stroke(): void;
46
+ fillRect(x: number, y: number, width: number, height: number): void;
47
+ clearRect(x: number, y: number, width: number, height: number): void;
48
+ clip(): void;
49
+ setLineDash(segments: readonly number[]): void;
50
+ createLinearGradient(x0: number, y0: number, x1: number, y1: number): IGradient;
51
+ createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): IGradient;
52
+ fillText(text: string, x: number, y: number, maxWidth?: number): void;
53
+ measureText(text: string): {
54
+ width: number;
55
+ };
56
+ }
57
+ /**
58
+ * ICanvas2D 的便捷判断:任意对象只要实现了最小方法集即视为满足契约。
59
+ * 用于适配器防御与测试 mock 的类型断言,不承担运行时校验。
60
+ */
61
+ export declare function isCanvas2DLike(value: unknown): value is ICanvas2D;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * ChartBase:所有图表的抽象基类,负责统一渲染管线。
3
+ *
4
+ * 渲染管线(render):
5
+ * save → scale(dpr) → clear → 背景 → 布局计算
6
+ * → 标题 → 图例 → 笛卡尔坐标轴(折线/柱状)
7
+ * → drawSeries(子类策略) → restore
8
+ *
9
+ * 生命周期:setOption 增量更新 → render;resize 改逻辑尺寸 → render;
10
+ * destroy 释放。所有坐标均为逻辑像素(CSS px),DPR 由管线统一缩放。
11
+ */
12
+ import type { ICanvas2D } from './canvas';
13
+ import { type LayoutInput, type LayoutResult } from './layout';
14
+ import { type LegendItem } from './legend';
15
+ import { CategoryScale, LinearScale } from './scale';
16
+ import { type CategoryAxisOption, type ChartOption, type ChartRenderContext, type LegendOption, type TitleOption, type ValueAxisOption } from '../types';
17
+ export interface CartesianScales {
18
+ value: LinearScale;
19
+ category: CategoryScale;
20
+ }
21
+ /**
22
+ * 子类需要实现的钩子:
23
+ * - drawSeries:绘制数据系列(核心策略)
24
+ * - hasCartesianAxis:是否需要坐标轴(饼图/雷达返回 false)
25
+ * - getValueDomain:数值轴数据域(有轴时)
26
+ * - getCategoryLabels:类目轴标签(有轴时)
27
+ * - isHorizontal:横向布局(横向柱状图)
28
+ */
29
+ export declare abstract class ChartBase<T extends ChartOption> {
30
+ protected readonly ctx: ICanvas2D;
31
+ protected option: T;
32
+ protected width: number;
33
+ protected height: number;
34
+ protected dpr: number;
35
+ protected readonly palette: readonly string[];
36
+ /** 每次 render 构建的笛卡尔比例尺,供 drawSeries 读取。 */
37
+ protected cartesian: CartesianScales | null;
38
+ private destroyed;
39
+ constructor(context: ChartRenderContext, option: T);
40
+ setOption(option: Partial<T>): this;
41
+ resize(width: number, height: number): this;
42
+ render(): this;
43
+ destroy(): void;
44
+ isDestroyed(): boolean;
45
+ protected abstract drawSeries(ctx: ICanvas2D, layout: LayoutResult, option: T): void;
46
+ protected hasCartesianAxis(): boolean;
47
+ protected getValueDomain(): {
48
+ min: number;
49
+ max: number;
50
+ };
51
+ protected getCategoryLabels(): readonly string[];
52
+ protected isHorizontal(): boolean;
53
+ protected getBackgroundColor(): string | undefined;
54
+ protected getTitle(): TitleOption | undefined;
55
+ protected getLegend(): LegendOption | undefined;
56
+ protected getLegendItems(): LegendItem[];
57
+ protected getValueAxis(): ValueAxisOption | undefined;
58
+ protected getCategoryAxis(): CategoryAxisOption | undefined;
59
+ protected computeLayout(): LayoutResult;
60
+ protected buildLayoutInput(): LayoutInput;
61
+ /** 用占位 range 预计算数值刻度标签(仅用于测量,不参与最终映射)。 */
62
+ protected computeValueLabels(): string[];
63
+ protected drawTitle(layout: LayoutResult): void;
64
+ protected drawLegend(layout: LayoutResult): void;
65
+ protected buildCartesianScales(layout: LayoutResult): CartesianScales;
66
+ protected drawCartesianAxis(layout: LayoutResult): void;
67
+ /** 系列取色:显式 color 优先,否则按索引取色板。 */
68
+ protected seriesColor(index: number, color?: string): string;
69
+ }