transone-chart 0.1.2 → 0.1.4
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 +5 -3
- package/dist/component.d.ts +10 -1
- package/dist/core/chart.d.ts +1 -1
- package/dist/core/debounce.d.ts +17 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -4
- package/dist/index.js.map +9 -8
- package/dist/types.d.ts +8 -0
- package/lib/charts/bar.ts +12 -6
- package/lib/charts/pie.ts +46 -9
- package/lib/component.ts +112 -5
- package/lib/core/chart.ts +5 -1
- package/lib/core/debounce.ts +52 -0
- package/lib/index.ts +4 -0
- package/lib/types.ts +8 -0
- package/package.json +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -126,6 +126,14 @@ export interface PieChartOption {
|
|
|
126
126
|
innerRadius?: number | string;
|
|
127
127
|
/** 是否显示扇区标签(名称 + 百分比),默认 true。 */
|
|
128
128
|
showLabel?: boolean;
|
|
129
|
+
/** 标签位置:'inside' 画在扇区内(默认,白字);'outside' 画在扇区外并带引线(适合小扇区 / 长名称)。 */
|
|
130
|
+
labelPosition?: 'inside' | 'outside';
|
|
131
|
+
/** 外部标签引线沿平分线的长度(px),仅 labelPosition: 'outside' 生效,默认 14。 */
|
|
132
|
+
labelLineLength?: number;
|
|
133
|
+
/** 外部标签与引线端点的水平间距(px),仅 labelPosition: 'outside' 生效,默认 6。 */
|
|
134
|
+
labelGap?: number;
|
|
135
|
+
/** 外部标签引线颜色,默认 '#c0c4cc'。 */
|
|
136
|
+
labelLineColor?: string;
|
|
129
137
|
labelFontSize?: number;
|
|
130
138
|
labelColor?: string;
|
|
131
139
|
/** 起始角(弧度),默认 -Math.PI / 2(12 点方向),顺时针。 */
|
package/lib/charts/bar.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import type { ICanvas2D } from '../core/canvas';
|
|
11
11
|
import { ChartBase } from '../core/chart';
|
|
12
|
+
import type { CategoryScale } from '../core/scale';
|
|
12
13
|
import type { LayoutResult } from '../core/layout';
|
|
13
14
|
import type { LegendItem } from '../core/legend';
|
|
14
15
|
import type { BarChartOption, BarSeries } from '../types';
|
|
@@ -154,20 +155,25 @@ export class BarChart extends ChartBase<BarChartOption> {
|
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
private computeSlot(
|
|
157
|
-
category:
|
|
158
|
+
category: CategoryScale,
|
|
158
159
|
groupCount: number,
|
|
159
160
|
explicitWidth?: number
|
|
160
161
|
): Slot {
|
|
162
|
+
const band = category.bandWidth;
|
|
163
|
+
const inner = category.innerWidth();
|
|
161
164
|
if (explicitWidth !== undefined) {
|
|
162
|
-
//
|
|
165
|
+
// 显式柱宽:整组在类目带内居中
|
|
163
166
|
return {
|
|
164
|
-
offset: (
|
|
167
|
+
offset: (band - explicitWidth * groupCount) / 2,
|
|
165
168
|
size: explicitWidth,
|
|
166
169
|
};
|
|
167
170
|
}
|
|
168
|
-
const size =
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
const size = (inner / Math.max(1, groupCount)) * 0.8;
|
|
172
|
+
// 柱区(size × groupCount)先在 inner 内居中,再随 inner 在类目带内居中,
|
|
173
|
+
// 保证整组柱中心落在类目中心(与轴标签 center(i) 对齐)。
|
|
174
|
+
const bandSide = (band - inner) / 2;
|
|
175
|
+
const innerSide = (inner - size * groupCount) / 2;
|
|
176
|
+
return { offset: bandSide + innerSide, size };
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
private drawBar(
|
package/lib/charts/pie.ts
CHANGED
|
@@ -77,19 +77,56 @@ export class PieChart extends ChartBase<PieChartOption> {
|
|
|
77
77
|
ctx.stroke();
|
|
78
78
|
ctx.restore();
|
|
79
79
|
|
|
80
|
-
// 扇区标签:名称 +
|
|
80
|
+
// 扇区标签:名称 + 百分比
|
|
81
|
+
// - inside(默认):画在扇区角平分线上(白字)
|
|
82
|
+
// - outside:引线把文字引到扇区外(右半区左对齐、左半区右对齐,避免文字跨越中线)
|
|
81
83
|
if (option.showLabel !== false) {
|
|
82
84
|
const midAngle = angle + sweep / 2;
|
|
83
|
-
const labelRadius = (outerRadius + (innerRadius > 0 ? innerRadius : 0)) / 2 * 0.85;
|
|
84
|
-
const labelX = centerX + Math.cos(midAngle) * labelRadius;
|
|
85
|
-
const labelY = centerY + Math.sin(midAngle) * labelRadius;
|
|
86
85
|
const percent = total > 0 ? Math.round((d.value / total) * 100) : 0;
|
|
86
|
+
const text = `${d.name} ${percent}%`;
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
if (option.labelPosition === 'outside') {
|
|
89
|
+
const lineLength = option.labelLineLength ?? 14;
|
|
90
|
+
const gap = option.labelGap ?? 6;
|
|
91
|
+
const dirX = Math.cos(midAngle);
|
|
92
|
+
const dirY = Math.sin(midAngle);
|
|
93
|
+
// 引线第一段:扇区边缘沿平分线向外
|
|
94
|
+
const edgeX = centerX + dirX * outerRadius;
|
|
95
|
+
const edgeY = centerY + dirY * outerRadius;
|
|
96
|
+
const bendX = centerX + dirX * (outerRadius + lineLength);
|
|
97
|
+
const bendY = centerY + dirY * (outerRadius + lineLength);
|
|
98
|
+
// 第二段:水平延伸到文字锚点(右半向右、左半向左)
|
|
99
|
+
const horizontal = dirX >= 0 ? 1 : -1;
|
|
100
|
+
const textX = bendX + horizontal * gap;
|
|
101
|
+
const textY = bendY;
|
|
102
|
+
|
|
103
|
+
ctx.save();
|
|
104
|
+
ctx.strokeStyle = option.labelLineColor ?? '#c0c4cc';
|
|
105
|
+
ctx.lineWidth = 1;
|
|
106
|
+
ctx.beginPath();
|
|
107
|
+
ctx.moveTo(edgeX, edgeY);
|
|
108
|
+
ctx.lineTo(bendX, bendY);
|
|
109
|
+
ctx.lineTo(textX, textY);
|
|
110
|
+
ctx.stroke();
|
|
111
|
+
ctx.restore();
|
|
112
|
+
|
|
113
|
+
applyFont(ctx, option.labelFontSize ?? 10);
|
|
114
|
+
ctx.fillStyle = option.labelColor ?? '#323233';
|
|
115
|
+
ctx.textAlign = horizontal > 0 ? 'left' : 'right';
|
|
116
|
+
ctx.textBaseline = 'middle';
|
|
117
|
+
ctx.fillText(text, textX, textY);
|
|
118
|
+
} else {
|
|
119
|
+
const labelRadius =
|
|
120
|
+
((outerRadius + (innerRadius > 0 ? innerRadius : 0)) / 2) * 0.85;
|
|
121
|
+
const labelX = centerX + Math.cos(midAngle) * labelRadius;
|
|
122
|
+
const labelY = centerY + Math.sin(midAngle) * labelRadius;
|
|
123
|
+
|
|
124
|
+
applyFont(ctx, option.labelFontSize ?? 10);
|
|
125
|
+
ctx.fillStyle = option.labelColor ?? '#ffffff';
|
|
126
|
+
ctx.textAlign = 'center';
|
|
127
|
+
ctx.textBaseline = 'middle';
|
|
128
|
+
ctx.fillText(text, labelX, labelY);
|
|
129
|
+
}
|
|
93
130
|
}
|
|
94
131
|
|
|
95
132
|
angle += sweep;
|
package/lib/component.ts
CHANGED
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
* - 未来原生:通过 resolve 注入自定义解析器
|
|
8
8
|
*
|
|
9
9
|
* 组件本身是薄壳:状态由父级以 option 传入(完全受控),
|
|
10
|
-
* 数据变化时自动 setOption + render
|
|
10
|
+
* 数据变化时自动 setOption + render;
|
|
11
|
+
* 容器 / 窗口尺寸变化时防抖(150ms)自动重绘:
|
|
12
|
+
* - Web:ResizeObserver 观察 canvas,重测 clientWidth/Height 后增量 resize + render
|
|
13
|
+
* - 小程序:平台窗口尺寸回调触发后重建图表(SelectorQuery 取最新节点尺寸)
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
16
|
import {
|
|
@@ -17,12 +20,15 @@ import {
|
|
|
17
20
|
} from 'transone';
|
|
18
21
|
import type { ChartRenderContext, ChartOption } from './types';
|
|
19
22
|
import type { ChartBase } from './core/chart';
|
|
23
|
+
import { debounce, type Debounced } from './core/debounce';
|
|
20
24
|
import { createChart } from './factory';
|
|
21
25
|
import {
|
|
22
26
|
detectMiniProgramGlobal,
|
|
23
27
|
getMiniProgramCanvasNode,
|
|
28
|
+
getMiniProgramGlobal,
|
|
24
29
|
resolveMiniProgramCanvas,
|
|
25
30
|
} from './adapters/miniprogram';
|
|
31
|
+
import { detectPixelRatio } from './adapters/types';
|
|
26
32
|
import { resolveWebCanvas } from './adapters/web';
|
|
27
33
|
|
|
28
34
|
export interface TcChartProps {
|
|
@@ -42,9 +48,20 @@ interface TcChartState {
|
|
|
42
48
|
/** canvas 元素 id:Web 端仅作 DOM 属性;小程序端配合 SelectorQuery .in(实例) 隔离查询,多实例共存不冲突。 */
|
|
43
49
|
const CANVAS_ID = 'tc-chart-canvas';
|
|
44
50
|
|
|
51
|
+
/** 尺寸变化自动重绘的防抖窗口(毫秒)。连续 resize 事件只在此窗口后重绘一次。 */
|
|
52
|
+
const RESIZE_DEBOUNCE_MS = 150;
|
|
53
|
+
|
|
54
|
+
interface MpWindowResizeApi {
|
|
55
|
+
onWindowResize?: (callback: () => void) => unknown;
|
|
56
|
+
offWindowResize?: (callback: () => void) => unknown;
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
export class TcChart extends Component<TcChartProps, TcChartState> {
|
|
46
60
|
private chart: ChartBase<ChartOption> | null = null;
|
|
47
61
|
private destroyed = false;
|
|
62
|
+
private resizeDebounced: Debounced<() => void> | null = null;
|
|
63
|
+
private resizeObserver: ResizeObserver | null = null;
|
|
64
|
+
private mpResizeHandler: (() => void) | null = null;
|
|
48
65
|
|
|
49
66
|
protected initState(): TcChartState {
|
|
50
67
|
return { canvasId: CANVAS_ID };
|
|
@@ -71,6 +88,7 @@ export class TcChart extends Component<TcChartProps, TcChartState> {
|
|
|
71
88
|
|
|
72
89
|
protected onMounted(): void {
|
|
73
90
|
this.attachChart();
|
|
91
|
+
this.setupAutoResize();
|
|
74
92
|
}
|
|
75
93
|
|
|
76
94
|
protected onUpdated(): void {
|
|
@@ -82,6 +100,7 @@ export class TcChart extends Component<TcChartProps, TcChartState> {
|
|
|
82
100
|
}
|
|
83
101
|
|
|
84
102
|
protected onUnmounted(): void {
|
|
103
|
+
this.teardownAutoResize();
|
|
85
104
|
this.destroyed = true;
|
|
86
105
|
this.chart?.destroy();
|
|
87
106
|
this.chart = null;
|
|
@@ -98,7 +117,8 @@ export class TcChart extends Component<TcChartProps, TcChartState> {
|
|
|
98
117
|
}
|
|
99
118
|
this.resolveContext()
|
|
100
119
|
.then((context) => {
|
|
101
|
-
if (this.destroyed) {
|
|
120
|
+
if (this.destroyed || !context) {
|
|
121
|
+
// 非浏览器环境(SSR / 构建期静态渲染)或显式跳过:渲染交给客户端水合阶段。
|
|
102
122
|
return;
|
|
103
123
|
}
|
|
104
124
|
this.chart = createChart(context, this.props.option);
|
|
@@ -114,7 +134,88 @@ export class TcChart extends Component<TcChartProps, TcChartState> {
|
|
|
114
134
|
return super.getElement() as HTMLCanvasElement | null;
|
|
115
135
|
}
|
|
116
136
|
|
|
117
|
-
|
|
137
|
+
/* —— 自动重绘:容器 / 窗口尺寸变化时防抖重绘 —— */
|
|
138
|
+
|
|
139
|
+
private setupAutoResize(): void {
|
|
140
|
+
if (this.destroyed || this.resizeDebounced) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.resizeDebounced = debounce(() => this.handleResize(), RESIZE_DEBOUNCE_MS);
|
|
144
|
+
|
|
145
|
+
const platform = detectMiniProgramGlobal();
|
|
146
|
+
if (platform) {
|
|
147
|
+
// 小程序:各平台均有窗口尺寸回调(横竖屏切换 / 分屏等场景)
|
|
148
|
+
const mp = getMiniProgramGlobal(platform) as MpWindowResizeApi | null;
|
|
149
|
+
if (mp && typeof mp.onWindowResize === 'function') {
|
|
150
|
+
this.mpResizeHandler = () => this.resizeDebounced!.run();
|
|
151
|
+
mp.onWindowResize(this.mpResizeHandler);
|
|
152
|
+
}
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Web:ResizeObserver 观察 canvas 自身(CSS 宽高 100%,容器变化即触发)。
|
|
157
|
+
// SSR / 构建期无真实元素与 ResizeObserver,跳过。
|
|
158
|
+
const element = this.getElement();
|
|
159
|
+
if (
|
|
160
|
+
element &&
|
|
161
|
+
typeof element.getContext === 'function' &&
|
|
162
|
+
typeof ResizeObserver === 'function'
|
|
163
|
+
) {
|
|
164
|
+
this.resizeObserver = new ResizeObserver(() => this.resizeDebounced!.run());
|
|
165
|
+
this.resizeObserver.observe(element);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private teardownAutoResize(): void {
|
|
170
|
+
this.resizeObserver?.disconnect();
|
|
171
|
+
this.resizeObserver = null;
|
|
172
|
+
this.resizeDebounced?.cancel();
|
|
173
|
+
this.resizeDebounced = null;
|
|
174
|
+
if (this.mpResizeHandler) {
|
|
175
|
+
const platform = detectMiniProgramGlobal();
|
|
176
|
+
const mp = platform
|
|
177
|
+
? (getMiniProgramGlobal(platform) as MpWindowResizeApi | null)
|
|
178
|
+
: null;
|
|
179
|
+
if (mp && typeof mp.offWindowResize === 'function') {
|
|
180
|
+
mp.offWindowResize(this.mpResizeHandler);
|
|
181
|
+
}
|
|
182
|
+
this.mpResizeHandler = null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private handleResize(): void {
|
|
187
|
+
if (this.destroyed) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (detectMiniProgramGlobal()) {
|
|
191
|
+
// 小程序:窗口尺寸变化后 SelectorQuery 取到的节点尺寸已更新,
|
|
192
|
+
// 重建图表即可拿到最新尺寸(上下文持有节点实测宽高与 pixelRatio)。
|
|
193
|
+
this.chart?.destroy();
|
|
194
|
+
this.chart = null;
|
|
195
|
+
this.attachChart();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const element = this.getElement() as HTMLCanvasElement | null;
|
|
199
|
+
if (!element || typeof element.getContext !== 'function') {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const width = element.clientWidth || 0;
|
|
203
|
+
const height = element.clientHeight || 0;
|
|
204
|
+
if (width <= 0 || height <= 0) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (!this.chart) {
|
|
208
|
+
this.attachChart();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// 更新物理像素缓冲(逻辑尺寸 × DPR),并增量重绘(保留图表状态)
|
|
212
|
+
const dpr = detectPixelRatio();
|
|
213
|
+
element.width = Math.round(width * dpr);
|
|
214
|
+
element.height = Math.round(height * dpr);
|
|
215
|
+
this.chart.resize(width, height, dpr).render();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private resolveContext(): Promise<ChartRenderContext | null> {
|
|
118
219
|
if (this.props.resolve) {
|
|
119
220
|
return Promise.resolve(this.props.resolve(this.getElement()));
|
|
120
221
|
}
|
|
@@ -141,9 +242,15 @@ export class TcChart extends Component<TcChartProps, TcChartState> {
|
|
|
141
242
|
return attempt(5);
|
|
142
243
|
}
|
|
143
244
|
|
|
144
|
-
// Web:canvas 元素即 HTMLCanvasElement
|
|
245
|
+
// Web:canvas 元素即 HTMLCanvasElement。SSR / 构建期静态渲染环境下
|
|
246
|
+
// 元素没有真实 2d context(如 projectDom),返回 null 跳过渲染,
|
|
247
|
+
// 由客户端水合阶段(真实浏览器)完成绘制。
|
|
248
|
+
const element = this.getElement() as HTMLCanvasElement | null;
|
|
249
|
+
if (!element || typeof element.getContext !== 'function') {
|
|
250
|
+
return Promise.resolve(null);
|
|
251
|
+
}
|
|
145
252
|
return Promise.resolve(
|
|
146
|
-
resolveWebCanvas(
|
|
253
|
+
resolveWebCanvas(element)
|
|
147
254
|
);
|
|
148
255
|
}
|
|
149
256
|
}
|
package/lib/core/chart.ts
CHANGED
|
@@ -72,9 +72,13 @@ export abstract class ChartBase<T extends ChartOption> {
|
|
|
72
72
|
return this;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
public resize(width: number, height: number): this {
|
|
75
|
+
public resize(width: number, height: number, dpr?: number): this {
|
|
76
76
|
this.width = Math.max(0, width);
|
|
77
77
|
this.height = Math.max(0, height);
|
|
78
|
+
// dpr 变化(如窗口跨屏移动)时同步更新,绘制管线按新 dpr 缩放
|
|
79
|
+
if (dpr !== undefined && Number.isFinite(dpr) && dpr > 0) {
|
|
80
|
+
this.dpr = dpr;
|
|
81
|
+
}
|
|
78
82
|
return this;
|
|
79
83
|
}
|
|
80
84
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 零依赖防抖工具。
|
|
3
|
+
*
|
|
4
|
+
* 用于高频触发场景(如窗口 / 容器尺寸变化)的合并调度:
|
|
5
|
+
* 连续调用只会在停止触发 delay 毫秒后执行一次。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface Debounced<T extends (...args: never[]) => unknown> {
|
|
9
|
+
/** 触发一次;若已有待执行调用则重置计时器(合并连续触发)。 */
|
|
10
|
+
run: (...args: Parameters<T>) => void;
|
|
11
|
+
/** 取消尚未执行的调用。 */
|
|
12
|
+
cancel: () => void;
|
|
13
|
+
/** 是否已有待执行的调用。 */
|
|
14
|
+
pending: () => boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type AnyFunction = (...args: any[]) => unknown;
|
|
18
|
+
|
|
19
|
+
export function debounce<T extends AnyFunction>(
|
|
20
|
+
fn: T,
|
|
21
|
+
delay: number
|
|
22
|
+
): Debounced<T> {
|
|
23
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
24
|
+
let lastArgs: Parameters<T> | null = null;
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
run(...args: Parameters<T>): void {
|
|
28
|
+
lastArgs = args;
|
|
29
|
+
if (timer !== null) {
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
}
|
|
32
|
+
timer = setTimeout(() => {
|
|
33
|
+
timer = null;
|
|
34
|
+
if (lastArgs !== null) {
|
|
35
|
+
const runArgs = lastArgs;
|
|
36
|
+
lastArgs = null;
|
|
37
|
+
fn(...runArgs);
|
|
38
|
+
}
|
|
39
|
+
}, delay);
|
|
40
|
+
},
|
|
41
|
+
cancel(): void {
|
|
42
|
+
if (timer !== null) {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
timer = null;
|
|
45
|
+
}
|
|
46
|
+
lastArgs = null;
|
|
47
|
+
},
|
|
48
|
+
pending(): boolean {
|
|
49
|
+
return timer !== null;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
package/lib/index.ts
CHANGED
|
@@ -30,6 +30,10 @@ export type { LinearScaleOptions, ScaleResult } from './core/scale';
|
|
|
30
30
|
export { computeLayout } from './core/layout';
|
|
31
31
|
export type { Box, LayoutInput, LayoutResult, Padding } from './core/layout';
|
|
32
32
|
|
|
33
|
+
// 工具:防抖(自动重绘等高频触发场景)
|
|
34
|
+
export { debounce } from './core/debounce';
|
|
35
|
+
export type { Debounced } from './core/debounce';
|
|
36
|
+
|
|
33
37
|
// 图表基类
|
|
34
38
|
export { ChartBase } from './core/chart';
|
|
35
39
|
export type { CartesianScales } from './core/chart';
|
package/lib/types.ts
CHANGED
|
@@ -154,6 +154,14 @@ export interface PieChartOption {
|
|
|
154
154
|
innerRadius?: number | string;
|
|
155
155
|
/** 是否显示扇区标签(名称 + 百分比),默认 true。 */
|
|
156
156
|
showLabel?: boolean;
|
|
157
|
+
/** 标签位置:'inside' 画在扇区内(默认,白字);'outside' 画在扇区外并带引线(适合小扇区 / 长名称)。 */
|
|
158
|
+
labelPosition?: 'inside' | 'outside';
|
|
159
|
+
/** 外部标签引线沿平分线的长度(px),仅 labelPosition: 'outside' 生效,默认 14。 */
|
|
160
|
+
labelLineLength?: number;
|
|
161
|
+
/** 外部标签与引线端点的水平间距(px),仅 labelPosition: 'outside' 生效,默认 6。 */
|
|
162
|
+
labelGap?: number;
|
|
163
|
+
/** 外部标签引线颜色,默认 '#c0c4cc'。 */
|
|
164
|
+
labelLineColor?: string;
|
|
157
165
|
labelFontSize?: number;
|
|
158
166
|
labelColor?: string;
|
|
159
167
|
/** 起始角(弧度),默认 -Math.PI / 2(12 点方向),顺时针。 */
|