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.
- package/README.md +181 -0
- package/dist/adapters/index.d.ts +7 -0
- package/dist/adapters/miniprogram.d.ts +44 -0
- package/dist/adapters/native.d.ts +28 -0
- package/dist/adapters/types.d.ts +27 -0
- package/dist/adapters/web.d.ts +13 -0
- package/dist/charts/bar.d.ts +25 -0
- package/dist/charts/index.d.ts +4 -0
- package/dist/charts/line.d.ts +20 -0
- package/dist/charts/pie.d.ts +14 -0
- package/dist/charts/radar.d.ts +16 -0
- package/dist/component.d.ts +41 -0
- package/dist/core/axis.d.ts +47 -0
- package/dist/core/canvas.d.ts +61 -0
- package/dist/core/chart.d.ts +69 -0
- package/dist/core/layout.d.ts +58 -0
- package/dist/core/legend.d.ts +26 -0
- package/dist/core/scale.d.ts +67 -0
- package/dist/core/text.d.ts +20 -0
- package/dist/factory.d.ts +22 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +49 -0
- package/dist/types.d.ts +180 -0
- package/lib/adapters/index.ts +18 -0
- package/lib/adapters/miniprogram.ts +146 -0
- package/lib/adapters/native.ts +39 -0
- package/lib/adapters/types.ts +44 -0
- package/lib/adapters/web.ts +41 -0
- package/lib/charts/bar.ts +270 -0
- package/lib/charts/index.ts +4 -0
- package/lib/charts/line.ts +153 -0
- package/lib/charts/pie.ts +110 -0
- package/lib/charts/radar.ts +166 -0
- package/lib/component.ts +149 -0
- package/lib/core/axis.ts +174 -0
- package/lib/core/canvas.ts +130 -0
- package/lib/core/chart.ts +322 -0
- package/lib/core/layout.ts +221 -0
- package/lib/core/legend.ts +94 -0
- package/lib/core/scale.ts +183 -0
- package/lib/core/text.ts +70 -0
- package/lib/factory.ts +62 -0
- package/lib/index.ts +69 -0
- package/lib/types.ts +218 -0
- package/package.json +55 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* transone-chart 公共类型定义。
|
|
3
|
+
*
|
|
4
|
+
* 所有图表共用一份 Option 风格配置(对齐 ECharts 心智:xAxis / yAxis /
|
|
5
|
+
* series / legend / title),但实现零依赖、纯 Canvas 2D 绘制。
|
|
6
|
+
*/
|
|
7
|
+
/** 一期支持的图表类型。 */
|
|
8
|
+
export type ChartType = 'line' | 'bar' | 'pie' | 'radar';
|
|
9
|
+
/** 默认主题色板(与 transone-ui 主色一致,可按需覆盖)。 */
|
|
10
|
+
export declare const DEFAULT_PALETTE: readonly string[];
|
|
11
|
+
export declare const DEFAULT_TEXT_COLOR = "#323233";
|
|
12
|
+
export declare const DEFAULT_AXIS_COLOR = "#c8c9cc";
|
|
13
|
+
export declare const DEFAULT_GRID_COLOR = "#ebedf0";
|
|
14
|
+
export declare const DEFAULT_FONT_FAMILY = "-apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Microsoft YaHei', sans-serif";
|
|
15
|
+
/** 标题配置。 */
|
|
16
|
+
export interface TitleOption {
|
|
17
|
+
text: string;
|
|
18
|
+
fontSize?: number;
|
|
19
|
+
color?: string;
|
|
20
|
+
/** 标题与绘图区之间的留白,默认 16。 */
|
|
21
|
+
padding?: number;
|
|
22
|
+
}
|
|
23
|
+
/** 图例配置。 */
|
|
24
|
+
export interface LegendOption {
|
|
25
|
+
show?: boolean;
|
|
26
|
+
/** 图例位置:顶部横向 / 底部横向 / 右侧纵向。 */
|
|
27
|
+
position?: 'top' | 'bottom' | 'right';
|
|
28
|
+
fontSize?: number;
|
|
29
|
+
color?: string;
|
|
30
|
+
/** 图例色块边长(正方形),默认 12。 */
|
|
31
|
+
markerSize?: number;
|
|
32
|
+
/** 图例项之间的水平间距,默认 16。 */
|
|
33
|
+
itemGap?: number;
|
|
34
|
+
}
|
|
35
|
+
/** 数值轴(y 轴/雷达数值)配置。 */
|
|
36
|
+
export interface ValueAxisOption {
|
|
37
|
+
/** 显式最小值;缺省自动 nice。 */
|
|
38
|
+
min?: number;
|
|
39
|
+
/** 显式最大值;缺省自动 nice。 */
|
|
40
|
+
max?: number;
|
|
41
|
+
/** 刻度分段数,默认 5。 */
|
|
42
|
+
splitCount?: number;
|
|
43
|
+
labelFontSize?: number;
|
|
44
|
+
labelColor?: string;
|
|
45
|
+
gridColor?: string;
|
|
46
|
+
gridLineWidth?: number;
|
|
47
|
+
/** 网格虚线,如 [4, 4];默认实线。 */
|
|
48
|
+
gridLineDash?: number[];
|
|
49
|
+
/** 是否显示网格,默认 true。 */
|
|
50
|
+
showGrid?: boolean;
|
|
51
|
+
/** 刻度标签格式化。 */
|
|
52
|
+
format?: (value: number) => string;
|
|
53
|
+
}
|
|
54
|
+
/** 类目轴(x 轴/雷达指标)配置。 */
|
|
55
|
+
export interface CategoryAxisOption {
|
|
56
|
+
labels: readonly string[];
|
|
57
|
+
labelFontSize?: number;
|
|
58
|
+
labelColor?: string;
|
|
59
|
+
gridColor?: string;
|
|
60
|
+
gridLineWidth?: number;
|
|
61
|
+
/** 是否显示类目网格竖线,默认 false。 */
|
|
62
|
+
showGrid?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** —— 折线图 —— */
|
|
65
|
+
export interface LineSeries {
|
|
66
|
+
name?: string;
|
|
67
|
+
data: readonly number[];
|
|
68
|
+
color?: string;
|
|
69
|
+
/** 平滑曲线(三次贝塞尔插值),默认 false。 */
|
|
70
|
+
smooth?: boolean;
|
|
71
|
+
/** 是否填充系列与数值轴之间的面积,默认 false。 */
|
|
72
|
+
area?: boolean;
|
|
73
|
+
/** 是否绘制数据点标记,默认 true。 */
|
|
74
|
+
showSymbol?: boolean;
|
|
75
|
+
lineWidth?: number;
|
|
76
|
+
}
|
|
77
|
+
export interface LineChartOption {
|
|
78
|
+
type: 'line';
|
|
79
|
+
title?: TitleOption;
|
|
80
|
+
legend?: LegendOption;
|
|
81
|
+
xAxis: CategoryAxisOption;
|
|
82
|
+
yAxis?: ValueAxisOption;
|
|
83
|
+
series: readonly LineSeries[];
|
|
84
|
+
/** 数值轴是否从 0 开始(数据全为正时强制含 0),默认 false(按数据范围紧凑显示)。 */
|
|
85
|
+
startFromZero?: boolean;
|
|
86
|
+
/** 背景色(如 'rgba(22,119,255,0.06)'),默认无。 */
|
|
87
|
+
backgroundColor?: string;
|
|
88
|
+
}
|
|
89
|
+
/** —— 柱状图 —— */
|
|
90
|
+
export interface BarSeries {
|
|
91
|
+
name?: string;
|
|
92
|
+
data: readonly number[];
|
|
93
|
+
color?: string;
|
|
94
|
+
/** 堆叠组名;同名系列纵向堆叠。 */
|
|
95
|
+
stack?: string;
|
|
96
|
+
/** 柱体宽度(px),缺省按类目带自动计算。 */
|
|
97
|
+
barWidth?: number;
|
|
98
|
+
/** 柱体圆角,默认 0。 */
|
|
99
|
+
borderRadius?: number;
|
|
100
|
+
}
|
|
101
|
+
export interface BarChartOption {
|
|
102
|
+
type: 'bar';
|
|
103
|
+
title?: TitleOption;
|
|
104
|
+
legend?: LegendOption;
|
|
105
|
+
xAxis: CategoryAxisOption;
|
|
106
|
+
yAxis?: ValueAxisOption;
|
|
107
|
+
series: readonly BarSeries[];
|
|
108
|
+
/** 横向柱状图(类目轴转纵向、数值轴转横向),默认 false。 */
|
|
109
|
+
horizontal?: boolean;
|
|
110
|
+
backgroundColor?: string;
|
|
111
|
+
}
|
|
112
|
+
/** —— 饼图 —— */
|
|
113
|
+
export interface PieDatum {
|
|
114
|
+
name: string;
|
|
115
|
+
value: number;
|
|
116
|
+
color?: string;
|
|
117
|
+
}
|
|
118
|
+
export interface PieChartOption {
|
|
119
|
+
type: 'pie';
|
|
120
|
+
data: readonly PieDatum[];
|
|
121
|
+
title?: TitleOption;
|
|
122
|
+
legend?: LegendOption;
|
|
123
|
+
/** 外半径:数字(px)或百分比字符串(相对 min(width, height) / 2),默认 '60%'。 */
|
|
124
|
+
radius?: number | string;
|
|
125
|
+
/** 内半径:0 为饼图;>0 为环形图,默认 0。 */
|
|
126
|
+
innerRadius?: number | string;
|
|
127
|
+
/** 是否显示扇区标签(名称 + 百分比),默认 true。 */
|
|
128
|
+
showLabel?: boolean;
|
|
129
|
+
labelFontSize?: number;
|
|
130
|
+
labelColor?: string;
|
|
131
|
+
/** 起始角(弧度),默认 -Math.PI / 2(12 点方向),顺时针。 */
|
|
132
|
+
startAngle?: number;
|
|
133
|
+
backgroundColor?: string;
|
|
134
|
+
}
|
|
135
|
+
/** —— 雷达图 —— */
|
|
136
|
+
export interface RadarIndicator {
|
|
137
|
+
name: string;
|
|
138
|
+
/** 指标最大值;缺省取所有系列该指标的最大值。 */
|
|
139
|
+
max?: number;
|
|
140
|
+
}
|
|
141
|
+
export interface RadarSeries {
|
|
142
|
+
name?: string;
|
|
143
|
+
data: readonly number[];
|
|
144
|
+
color?: string;
|
|
145
|
+
/** 是否填充多边形区域,默认 true。 */
|
|
146
|
+
area?: boolean;
|
|
147
|
+
lineWidth?: number;
|
|
148
|
+
}
|
|
149
|
+
export interface RadarChartOption {
|
|
150
|
+
type: 'radar';
|
|
151
|
+
indicators: readonly RadarIndicator[];
|
|
152
|
+
series: readonly RadarSeries[];
|
|
153
|
+
title?: TitleOption;
|
|
154
|
+
legend?: LegendOption;
|
|
155
|
+
/** 网格层数,默认 5。 */
|
|
156
|
+
splitCount?: number;
|
|
157
|
+
gridColor?: string;
|
|
158
|
+
gridLineWidth?: number;
|
|
159
|
+
labelFontSize?: number;
|
|
160
|
+
labelColor?: string;
|
|
161
|
+
/** 雷达中心半径(px),缺省取 min(width, height) / 2 的 60%。 */
|
|
162
|
+
radius?: number;
|
|
163
|
+
/** 起始角(弧度),默认 -Math.PI / 2(12 点方向),顺时针。 */
|
|
164
|
+
startAngle?: number;
|
|
165
|
+
backgroundColor?: string;
|
|
166
|
+
}
|
|
167
|
+
export type ChartOption = LineChartOption | BarChartOption | PieChartOption | RadarChartOption;
|
|
168
|
+
/** 图表渲染上下文(适配器解析后的产物)。 */
|
|
169
|
+
export interface ChartRenderContext {
|
|
170
|
+
/** 跨端 Canvas 2D 上下文。 */
|
|
171
|
+
ctx: import('./core/canvas').ICanvas2D;
|
|
172
|
+
/** 逻辑宽度(CSS px)。 */
|
|
173
|
+
width: number;
|
|
174
|
+
/** 逻辑高度(CSS px)。 */
|
|
175
|
+
height: number;
|
|
176
|
+
/** 设备像素比。 */
|
|
177
|
+
dpr: number;
|
|
178
|
+
/** 自定义色板,缺省使用 DEFAULT_PALETTE。 */
|
|
179
|
+
palette?: readonly string[];
|
|
180
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
ResolveCanvasOptions,
|
|
3
|
+
ResolvedCanvas,
|
|
4
|
+
} from './types';
|
|
5
|
+
export { detectPixelRatio } from './types';
|
|
6
|
+
|
|
7
|
+
export { resolveWebCanvas } from './web';
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
getMiniProgramCanvasNode,
|
|
11
|
+
getMiniProgramGlobal,
|
|
12
|
+
resolveMiniProgramCanvas,
|
|
13
|
+
detectMiniProgramPixelRatio,
|
|
14
|
+
} from './miniprogram';
|
|
15
|
+
export type { MiniProgramCanvasNode, MiniProgramGlobal } from './miniprogram';
|
|
16
|
+
|
|
17
|
+
export { resolveNativeCanvas } from './native';
|
|
18
|
+
export type { NativeCanvasHost } from './native';
|
|
@@ -0,0 +1,146 @@
|
|
|
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
|
+
|
|
12
|
+
import type { ChartRenderContext } from '../types';
|
|
13
|
+
import type { ICanvas2D } from '../core/canvas';
|
|
14
|
+
import { detectPixelRatio, type ResolveCanvasOptions } from './types';
|
|
15
|
+
|
|
16
|
+
/** 小程序 Canvas 2D 节点(各端 node 的公共形状)。 */
|
|
17
|
+
export interface MiniProgramCanvasNode {
|
|
18
|
+
getContext(type: '2d'): unknown;
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type MiniProgramGlobal = 'wx' | 'my' | 'tt';
|
|
24
|
+
|
|
25
|
+
/** 安全读取小程序全局(避免与官方类型定义冲突,模式同 transone request)。 */
|
|
26
|
+
export function getMiniProgramGlobal(
|
|
27
|
+
name: MiniProgramGlobal
|
|
28
|
+
): Record<string, unknown> | null {
|
|
29
|
+
const host = getHost();
|
|
30
|
+
const api = host[name];
|
|
31
|
+
return typeof api === 'object' && api !== null
|
|
32
|
+
? (api as Record<string, unknown>)
|
|
33
|
+
: null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 通过 SelectorQuery 获取 Canvas 2D 节点(组件/页面内调用)。
|
|
38
|
+
* 各端 API 形状一致:createSelectorQuery().in(instance).select(selector)
|
|
39
|
+
* .fields({ node: true, size: true }).exec(callback)
|
|
40
|
+
*/
|
|
41
|
+
export function getMiniProgramCanvasNode(options: {
|
|
42
|
+
/** 端标识,缺省自动探测(wx → my → tt)。 */
|
|
43
|
+
platform?: MiniProgramGlobal;
|
|
44
|
+
/** 组件/页面实例(.in() 入参)。 */
|
|
45
|
+
instance?: unknown;
|
|
46
|
+
/** canvas 选择器,如 '#chart'。 */
|
|
47
|
+
selector: string;
|
|
48
|
+
}): Promise<MiniProgramCanvasNode> {
|
|
49
|
+
const platform =
|
|
50
|
+
options.platform ?? detectMiniProgramGlobal() ?? 'wx';
|
|
51
|
+
const api = getMiniProgramGlobal(platform);
|
|
52
|
+
const queryFn = api?.createSelectorQuery;
|
|
53
|
+
if (typeof queryFn !== 'function') {
|
|
54
|
+
return Promise.reject(
|
|
55
|
+
new Error(`getMiniProgramCanvasNode: ${platform}.createSelectorQuery is not available`)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
let query = queryFn.call(api);
|
|
61
|
+
if (options.instance && typeof query.in === 'function') {
|
|
62
|
+
query = query.in(options.instance);
|
|
63
|
+
}
|
|
64
|
+
if (typeof query.select !== 'function') {
|
|
65
|
+
reject(new Error(`${platform}.createSelectorQuery().select is not available`));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
query
|
|
69
|
+
.select(options.selector)
|
|
70
|
+
.fields({ node: true, size: true })
|
|
71
|
+
.exec((res: unknown) => {
|
|
72
|
+
const first = Array.isArray(res) ? res[0] : undefined;
|
|
73
|
+
const node = first?.node as MiniProgramCanvasNode | undefined;
|
|
74
|
+
if (!node || typeof node.getContext !== 'function') {
|
|
75
|
+
reject(
|
|
76
|
+
new Error(
|
|
77
|
+
`getMiniProgramCanvasNode: canvas node not found for "${options.selector}"`
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
resolve(node);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 探测当前小程序全局(wx → my → tt),均不存在返回 null。 */
|
|
88
|
+
export function detectMiniProgramGlobal(): MiniProgramGlobal | null {
|
|
89
|
+
if (getMiniProgramGlobal('wx')) {
|
|
90
|
+
return 'wx';
|
|
91
|
+
}
|
|
92
|
+
if (getMiniProgramGlobal('my')) {
|
|
93
|
+
return 'my';
|
|
94
|
+
}
|
|
95
|
+
if (getMiniProgramGlobal('tt')) {
|
|
96
|
+
return 'tt';
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** 读取各端设备像素比(wx.getSystemInfoSync / my.getSystemInfoSync / tt.getSystemInfoSync)。 */
|
|
102
|
+
export function detectMiniProgramPixelRatio(platform?: MiniProgramGlobal): number {
|
|
103
|
+
const name = platform ?? detectMiniProgramGlobal() ?? 'wx';
|
|
104
|
+
const api = getMiniProgramGlobal(name);
|
|
105
|
+
const sync = api?.getSystemInfoSync;
|
|
106
|
+
if (typeof sync === 'function') {
|
|
107
|
+
try {
|
|
108
|
+
const info = sync.call(api) as { pixelRatio?: number };
|
|
109
|
+
if (typeof info.pixelRatio === 'number' && info.pixelRatio > 0) {
|
|
110
|
+
return info.pixelRatio;
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
// 探测失败回退通用探测
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return detectPixelRatio();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 从 Canvas 2D 节点解析渲染上下文。
|
|
121
|
+
* 物理画布尺寸由节点自身管理(width/height 已在平台侧按像素比设置),
|
|
122
|
+
* 此处只做归一化返回。
|
|
123
|
+
*/
|
|
124
|
+
export function resolveMiniProgramCanvas(
|
|
125
|
+
node: MiniProgramCanvasNode,
|
|
126
|
+
options: ResolveCanvasOptions = {}
|
|
127
|
+
): ChartRenderContext {
|
|
128
|
+
const ctx2d = node.getContext('2d');
|
|
129
|
+
if (!ctx2d) {
|
|
130
|
+
throw new Error('resolveMiniProgramCanvas: 2d context is not available');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const dpr = options.dpr ?? detectMiniProgramPixelRatio();
|
|
134
|
+
const width = options.width ?? node.width;
|
|
135
|
+
const height = options.height ?? node.height;
|
|
136
|
+
const ctx = ctx2d as unknown as ICanvas2D;
|
|
137
|
+
|
|
138
|
+
return { ctx, width, height, dpr, palette: options.palette };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function getHost(): Record<string, unknown> {
|
|
142
|
+
if (typeof globalThis !== 'undefined') {
|
|
143
|
+
return globalThis as unknown as Record<string, unknown>;
|
|
144
|
+
}
|
|
145
|
+
return {};
|
|
146
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
|
|
14
|
+
import type { ChartRenderContext } from '../types';
|
|
15
|
+
|
|
16
|
+
/** 未来原生端需实现的最小宿主接口。 */
|
|
17
|
+
export interface NativeCanvasHost {
|
|
18
|
+
/** 逻辑尺寸(CSS px)。 */
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
/** 设备像素比。 */
|
|
22
|
+
pixelRatio: number;
|
|
23
|
+
/** 原生绘图上下文(宿主实现为 ICanvas2D 契约)。 */
|
|
24
|
+
getContext(type: '2d'): unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 未来实现:原生 canvas 节点 → ChartRenderContext。
|
|
29
|
+
* 当前未接入任何原生端,调用会明确报错,避免静默降级。
|
|
30
|
+
*/
|
|
31
|
+
export function resolveNativeCanvas(
|
|
32
|
+
_host: NativeCanvasHost
|
|
33
|
+
): ChartRenderContext {
|
|
34
|
+
throw new Error(
|
|
35
|
+
'resolveNativeCanvas is not implemented yet. ' +
|
|
36
|
+
'Native app support (iOS/Android/HarmonyOS) is planned for a future phase; ' +
|
|
37
|
+
'implement ICanvas2D on the host side and fill in this resolver.'
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
|
|
13
|
+
import type { ChartRenderContext } from '../types';
|
|
14
|
+
|
|
15
|
+
export interface ResolveCanvasOptions {
|
|
16
|
+
/** 逻辑宽度(CSS px);缺省取节点实际尺寸。 */
|
|
17
|
+
width?: number;
|
|
18
|
+
/** 逻辑高度(CSS px);缺省取节点实际尺寸。 */
|
|
19
|
+
height?: number;
|
|
20
|
+
/** 设备像素比;缺省自动探测。 */
|
|
21
|
+
dpr?: number;
|
|
22
|
+
/** 自定义色板。 */
|
|
23
|
+
palette?: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ResolvedCanvas {
|
|
27
|
+
context: ChartRenderContext;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** 通用探测设备像素比(web 与小程序可用 globalThis 访问)。 */
|
|
31
|
+
export function detectPixelRatio(): number {
|
|
32
|
+
const host = getHost();
|
|
33
|
+
if (typeof host.devicePixelRatio === 'number' && host.devicePixelRatio > 0) {
|
|
34
|
+
return host.devicePixelRatio;
|
|
35
|
+
}
|
|
36
|
+
return 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getHost(): Record<string, unknown> {
|
|
40
|
+
if (typeof globalThis !== 'undefined') {
|
|
41
|
+
return globalThis as unknown as Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web 适配器:HTMLCanvasElement → ChartRenderContext。
|
|
3
|
+
*
|
|
4
|
+
* - 把物理画布尺寸设为 逻辑尺寸 × DPR,保证高分屏清晰
|
|
5
|
+
* - 上下文为标准 CanvasRenderingContext2D,天然满足 ICanvas2D 契约
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ChartRenderContext } from '../types';
|
|
9
|
+
import type { ICanvas2D } from '../core/canvas';
|
|
10
|
+
import { detectPixelRatio, type ResolveCanvasOptions } from './types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 从 HTMLCanvasElement 解析渲染上下文。
|
|
14
|
+
* 会就地修改 canvas.width / canvas.height 为物理像素尺寸。
|
|
15
|
+
*/
|
|
16
|
+
export function resolveWebCanvas(
|
|
17
|
+
canvas: HTMLCanvasElement,
|
|
18
|
+
options: ResolveCanvasOptions = {}
|
|
19
|
+
): ChartRenderContext {
|
|
20
|
+
if (!canvas) {
|
|
21
|
+
throw new Error('resolveWebCanvas: canvas element is required');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const ctx2d = canvas.getContext('2d');
|
|
25
|
+
if (!ctx2d) {
|
|
26
|
+
throw new Error('resolveWebCanvas: 2d context is not available');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const dpr = options.dpr ?? detectPixelRatio();
|
|
30
|
+
const width = options.width ?? (canvas.clientWidth || canvas.width);
|
|
31
|
+
const height = options.height ?? (canvas.clientHeight || canvas.height);
|
|
32
|
+
|
|
33
|
+
// 物理像素 = 逻辑像素 × DPR
|
|
34
|
+
canvas.width = Math.round(width * dpr);
|
|
35
|
+
canvas.height = Math.round(height * dpr);
|
|
36
|
+
|
|
37
|
+
// 标准 CanvasRenderingContext2D 已满足 ICanvas2D 全部成员
|
|
38
|
+
const ctx = ctx2d as unknown as ICanvas2D;
|
|
39
|
+
|
|
40
|
+
return { ctx, width, height, dpr, palette: options.palette };
|
|
41
|
+
}
|