@agions/taroviz 1.11.1 → 2.0.3
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/CHANGELOG.md +245 -0
- package/README.md +104 -302
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/vendors.js +1 -0
- package/dist/cjs/vendors~echarts.js +1 -0
- package/dist/esm/index.js +1 -58151
- package/dist/esm/vendors.js +1 -0
- package/dist/esm/vendors~echarts.js +1 -0
- package/package.json +19 -25
- package/src/adapters/MiniAppAdapter.ts +136 -0
- package/src/adapters/__tests__/index.test.ts +1 -1
- package/src/adapters/h5/__tests__/index.test.ts +4 -2
- package/src/adapters/h5/index.ts +63 -64
- package/src/adapters/harmony/index.ts +23 -245
- package/src/adapters/index.ts +49 -45
- package/src/adapters/swan/index.ts +6 -69
- package/src/adapters/tt/index.ts +7 -70
- package/src/adapters/types.ts +25 -58
- package/src/adapters/weapp/index.ts +6 -69
- package/src/charts/__tests__/testUtils.tsx +87 -0
- package/src/charts/boxplot/__tests__/index.test.tsx +49 -103
- package/src/charts/boxplot/index.tsx +2 -1
- package/src/charts/boxplot/types.ts +17 -16
- package/src/charts/common/BaseChartWrapper.tsx +90 -82
- package/src/charts/common/__mocks__/BaseChartWrapper.tsx +17 -0
- package/src/charts/createChartComponent.tsx +36 -0
- package/src/charts/createOptionChartComponent.tsx +32 -0
- package/src/charts/funnel/__tests__/index.test.tsx +99 -0
- package/src/charts/funnel/index.tsx +60 -10
- package/src/charts/funnel/types.ts +6 -0
- package/src/charts/graph/__tests__/index.test.tsx +102 -33
- package/src/charts/graph/index.tsx +66 -9
- package/src/charts/graph/types.ts +6 -0
- package/src/charts/heatmap/__tests__/index.test.tsx +139 -0
- package/src/charts/heatmap/index.tsx +103 -10
- package/src/charts/heatmap/types.ts +6 -0
- package/src/charts/index.ts +74 -26
- package/src/charts/liquid/__tests__/index.test.tsx +52 -0
- package/src/charts/liquid/index.tsx +239 -182
- package/src/charts/liquid/types.ts +11 -11
- package/src/charts/parallel/__tests__/index.test.tsx +40 -67
- package/src/charts/parallel/index.tsx +2 -1
- package/src/charts/parallel/types.ts +19 -18
- package/src/charts/radar/__tests__/index.test.tsx +210 -0
- package/src/charts/radar/index.tsx +143 -10
- package/src/charts/radar/types.ts +13 -0
- package/src/charts/sankey/__tests__/index.test.tsx +124 -0
- package/src/charts/sankey/index.tsx +62 -10
- package/src/charts/sankey/types.ts +6 -0
- package/src/charts/tree/__tests__/index.test.tsx +71 -0
- package/src/charts/tree/index.tsx +5 -2
- package/src/charts/tree/types.ts +9 -9
- package/src/charts/types.ts +208 -106
- package/src/charts/utils.ts +9 -7
- package/src/charts/wordcloud/__tests__/index.test.tsx +98 -31
- package/src/charts/wordcloud/index.tsx +75 -9
- package/src/charts/wordcloud/types.ts +6 -0
- package/src/components/DataFilter/index.tsx +32 -10
- package/src/core/animation/types.ts +6 -6
- package/src/core/components/Annotation.tsx +6 -7
- package/src/core/components/BaseChart.tsx +110 -168
- package/src/core/components/ErrorBoundary.tsx +17 -4
- package/src/core/components/LazyChart.tsx +54 -55
- package/src/core/components/hooks/index.ts +6 -2
- package/src/core/components/hooks/useChartInit.ts +6 -3
- package/src/core/components/hooks/usePerformance.ts +8 -2
- package/src/core/components/hooks/useVirtualScroll.ts +2 -1
- package/src/core/index.ts +1 -1
- package/src/core/themes/ThemeManager.ts +1 -1
- package/src/core/types/common.ts +2 -1
- package/src/core/types/index.ts +0 -12
- package/src/core/types/platform.ts +3 -5
- package/src/core/utils/__tests__/deepClone.test.ts +317 -0
- package/src/core/utils/__tests__/index.test.ts +2 -1
- package/src/core/utils/chartInstances.ts +13 -0
- package/src/core/utils/common.ts +20 -29
- package/src/core/utils/deepClone.ts +114 -0
- package/src/core/utils/download.ts +128 -0
- package/src/core/utils/drillDown.ts +34 -353
- package/src/core/utils/drillDownHelpers.ts +426 -0
- package/src/core/utils/events.ts +12 -0
- package/src/core/utils/export/ExportUtils.ts +36 -67
- package/src/core/utils/format.ts +44 -0
- package/src/core/utils/index.ts +21 -154
- package/src/core/utils/merge.ts +25 -0
- package/src/core/utils/performance/PerformanceAnalyzer.ts +38 -21
- package/src/core/utils/performance/hooks.ts +7 -0
- package/src/core/utils/performance/index.ts +2 -0
- package/src/{hooks → core/utils/performance}/useAnimation.ts +45 -41
- package/src/core/utils/performance/useDataZoom.ts +324 -0
- package/src/{hooks → core/utils/performance}/usePerformance.ts +49 -41
- package/src/core/utils/performance/usePerformanceHooks.ts +278 -0
- package/src/core/utils/performanceUtils.ts +310 -0
- package/src/core/utils/runtime.ts +190 -0
- package/src/core/utils/setOptionUtils.ts +59 -0
- package/src/core/version.ts +14 -0
- package/src/editor/EnhancedThemeEditor.tsx +362 -540
- package/src/editor/ThemeEditor.tsx +55 -321
- package/src/editor/components/ThemeBasicSettings.tsx +113 -0
- package/src/editor/components/ThemeColorEditor.tsx +105 -0
- package/src/editor/components/ThemeSelector.tsx +70 -0
- package/src/editor/hooks/useThemeEditorState.ts +201 -0
- package/src/editor/index.ts +10 -2
- package/src/hooks/__tests__/index.test.tsx +3 -1
- package/src/hooks/chartConnectHelpers.ts +341 -0
- package/src/hooks/index.ts +55 -660
- package/src/hooks/types.ts +189 -0
- package/src/hooks/useChartAutoResize.ts +73 -0
- package/src/hooks/useChartConnect.ts +92 -238
- package/src/hooks/useChartDownload.ts +25 -27
- package/src/hooks/useChartHistory.ts +34 -49
- package/src/hooks/useChartInit.ts +59 -0
- package/src/hooks/useChartOptions.ts +259 -0
- package/src/hooks/useChartPerformance.ts +109 -0
- package/src/hooks/useChartSelection.ts +52 -49
- package/src/hooks/useChartTheme.ts +51 -0
- package/src/hooks/useDataTransform.ts +19 -4
- package/src/hooks/utils/chartDownloadUtils.ts +40 -53
- package/src/hooks/utils/dataTransformUtils.ts +22 -0
- package/src/index.ts +48 -34
- package/src/main.tsx +4 -9
- package/src/react-dom.d.ts +3 -3
- package/src/themes/index.ts +30 -855
- package/src/themes/palettes/blue-green.ts +13 -0
- package/src/themes/palettes/chalk.ts +13 -0
- package/src/themes/palettes/cyber.ts +44 -0
- package/src/themes/palettes/dark.ts +52 -0
- package/src/themes/palettes/default.ts +52 -0
- package/src/themes/palettes/elegant.ts +34 -0
- package/src/themes/palettes/forest.ts +13 -0
- package/src/themes/palettes/glass.ts +49 -0
- package/src/themes/palettes/golden.ts +13 -0
- package/src/themes/palettes/neon.ts +43 -0
- package/src/themes/palettes/ocean.ts +39 -0
- package/src/themes/palettes/pastel.ts +37 -0
- package/src/themes/palettes/purple-passion.ts +13 -0
- package/src/themes/palettes/retro.ts +33 -0
- package/src/themes/palettes/sunset.ts +40 -0
- package/src/themes/palettes/walden.ts +13 -0
- package/src/themes/registry.ts +184 -0
- package/src/themes/types.ts +213 -0
- package/src/charts/bar/__tests__/index.test.tsx +0 -113
- package/src/charts/bar/index.tsx +0 -14
- package/src/charts/candlestick/__tests__/index.test.tsx +0 -40
- package/src/charts/candlestick/index.tsx +0 -13
- package/src/charts/gauge/index.tsx +0 -14
- package/src/charts/line/__tests__/index.test.tsx +0 -107
- package/src/charts/line/index.tsx +0 -15
- package/src/charts/pie/__tests__/index.test.tsx +0 -112
- package/src/charts/pie/index.tsx +0 -14
- package/src/charts/scatter/index.tsx +0 -14
- package/src/charts/sunburst/index.tsx +0 -18
- package/src/charts/treemap/index.tsx +0 -18
- package/src/core/utils/codeGenerator/CodeGenerator.ts +0 -669
- package/src/core/utils/codeGenerator/index.ts +0 -13
- package/src/core/utils/codeGenerator/types.ts +0 -198
- package/src/core/utils/configGenerator/ConfigGenerator.ts +0 -583
- package/src/core/utils/configGenerator/index.ts +0 -13
- package/src/core/utils/configGenerator/types.ts +0 -445
- package/src/core/utils/debug/DebugPanel.tsx +0 -637
- package/src/core/utils/debug/debugger.ts +0 -322
- package/src/core/utils/debug/index.ts +0 -21
- package/src/core/utils/debug/types.ts +0 -142
- package/src/hooks/useDataZoom.ts +0 -323
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useDataZoom - 数据缩放 Hook
|
|
3
|
+
* 控制图表的数据缩放区域(dataZoom),支持拖拽和滚轮缩放
|
|
4
|
+
*/
|
|
5
|
+
import { useRef, useCallback, useEffect, useMemo } from 'react';
|
|
6
|
+
import type { RefObject } from 'react';
|
|
7
|
+
import type { ChartInstance, DataZoomType, ZoomRange, EventHandler } from '../../../hooks/types';
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Hook 实现
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 使用图表数据缩放
|
|
15
|
+
* @param options 配置选项
|
|
16
|
+
* @returns dataZoom 操作接口
|
|
17
|
+
*/
|
|
18
|
+
export function useDataZoom(
|
|
19
|
+
options: {
|
|
20
|
+
type?: DataZoomType;
|
|
21
|
+
start?: number;
|
|
22
|
+
end?: number;
|
|
23
|
+
minSpan?: number;
|
|
24
|
+
maxSpan?: number;
|
|
25
|
+
zoomLock?: boolean;
|
|
26
|
+
throttle?: number;
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
brushSelect?: boolean;
|
|
29
|
+
zoomMode?: 'scale' | 'mix';
|
|
30
|
+
resetOnUnmount?: boolean;
|
|
31
|
+
onZoomChange?: (range: { start: number; end: number }) => void;
|
|
32
|
+
} = {}
|
|
33
|
+
): {
|
|
34
|
+
bindDataZoom: (chartInstance: ChartInstance) => void;
|
|
35
|
+
setZoomRange: (start: number, end: number) => void;
|
|
36
|
+
resetZoom: () => void;
|
|
37
|
+
getZoomRange: () => ZoomRange;
|
|
38
|
+
startValue: RefObject<number | string | Date | undefined>;
|
|
39
|
+
endValue: RefObject<number | string | Date | undefined>;
|
|
40
|
+
bindEvents: (chartInstance: ChartInstance) => void;
|
|
41
|
+
} {
|
|
42
|
+
const {
|
|
43
|
+
type = 'inside',
|
|
44
|
+
start = 0,
|
|
45
|
+
end = 100,
|
|
46
|
+
minSpan,
|
|
47
|
+
maxSpan,
|
|
48
|
+
zoomLock = false,
|
|
49
|
+
throttle = 16,
|
|
50
|
+
disabled = false,
|
|
51
|
+
brushSelect = false,
|
|
52
|
+
zoomMode = 'scale',
|
|
53
|
+
resetOnUnmount = false,
|
|
54
|
+
onZoomChange,
|
|
55
|
+
} = options;
|
|
56
|
+
|
|
57
|
+
// Refs
|
|
58
|
+
const chartRef = useRef<ChartInstance | null>(null);
|
|
59
|
+
const startValueRef = useRef<number | string | Date | undefined>(undefined);
|
|
60
|
+
const endValueRef = useRef<number | string | Date | undefined>(undefined);
|
|
61
|
+
const isBindingRef = useRef(false);
|
|
62
|
+
|
|
63
|
+
// 稳定的配置 refs(避免闭包陷阱)
|
|
64
|
+
const configRef = useRef({
|
|
65
|
+
type,
|
|
66
|
+
start,
|
|
67
|
+
end,
|
|
68
|
+
minSpan,
|
|
69
|
+
maxSpan,
|
|
70
|
+
zoomLock,
|
|
71
|
+
disabled,
|
|
72
|
+
brushSelect,
|
|
73
|
+
zoomMode,
|
|
74
|
+
throttle,
|
|
75
|
+
});
|
|
76
|
+
configRef.current = {
|
|
77
|
+
type,
|
|
78
|
+
start,
|
|
79
|
+
end,
|
|
80
|
+
minSpan,
|
|
81
|
+
maxSpan,
|
|
82
|
+
zoomLock,
|
|
83
|
+
disabled,
|
|
84
|
+
brushSelect,
|
|
85
|
+
zoomMode,
|
|
86
|
+
throttle,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// 事件处理 refs(避免重复绑定)
|
|
90
|
+
const onZoomChangeRef = useRef(onZoomChange);
|
|
91
|
+
onZoomChangeRef.current = onZoomChange;
|
|
92
|
+
|
|
93
|
+
// 节流处理器 ref
|
|
94
|
+
const throttledHandlerRef = useRef<EventHandler | null>(null);
|
|
95
|
+
const lastCallRef = useRef(0);
|
|
96
|
+
|
|
97
|
+
// 构建 dataZoom 配置
|
|
98
|
+
const buildDataZoomConfig = useCallback(() => {
|
|
99
|
+
const {
|
|
100
|
+
type: cfgType,
|
|
101
|
+
start: cfgStart,
|
|
102
|
+
end: cfgEnd,
|
|
103
|
+
minSpan: cfgMinSpan,
|
|
104
|
+
maxSpan: cfgMaxSpan,
|
|
105
|
+
zoomLock: cfgZoomLock,
|
|
106
|
+
disabled: cfgDisabled,
|
|
107
|
+
brushSelect: cfgBrushSelect,
|
|
108
|
+
zoomMode: cfgZoomMode,
|
|
109
|
+
} = configRef.current;
|
|
110
|
+
|
|
111
|
+
const config: Record<string, unknown>[] = [];
|
|
112
|
+
|
|
113
|
+
// 内置型 dataZoom
|
|
114
|
+
if (cfgType === 'inside' || cfgZoomMode === 'mix') {
|
|
115
|
+
config.push({
|
|
116
|
+
type: 'inside',
|
|
117
|
+
start: cfgStart,
|
|
118
|
+
end: cfgEnd,
|
|
119
|
+
zoomLock: cfgZoomLock,
|
|
120
|
+
disabled: cfgDisabled,
|
|
121
|
+
zoomOnMouseWheel: true,
|
|
122
|
+
moveOnMouseMove: false,
|
|
123
|
+
moveOnMouseWheel: false,
|
|
124
|
+
preventDefaultMouseMove: true,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 滑块型 dataZoom
|
|
129
|
+
if (cfgType === 'slider' || cfgZoomMode === 'mix') {
|
|
130
|
+
config.push({
|
|
131
|
+
type: 'slider',
|
|
132
|
+
start: cfgStart,
|
|
133
|
+
end: cfgEnd,
|
|
134
|
+
minSpan: cfgMinSpan,
|
|
135
|
+
maxSpan: cfgMaxSpan,
|
|
136
|
+
zoomLock: cfgZoomLock,
|
|
137
|
+
disabled: cfgDisabled,
|
|
138
|
+
show: true,
|
|
139
|
+
brushSelect: cfgBrushSelect,
|
|
140
|
+
throttle: configRef.current.throttle,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return config;
|
|
145
|
+
}, []);
|
|
146
|
+
|
|
147
|
+
// 绑定 dataZoom 到图表
|
|
148
|
+
const bindDataZoom = useCallback(
|
|
149
|
+
(chartInstance: ChartInstance) => {
|
|
150
|
+
if (!chartInstance || isBindingRef.current) return;
|
|
151
|
+
|
|
152
|
+
isBindingRef.current = true;
|
|
153
|
+
chartRef.current = chartInstance;
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
const dataZoomConfig = buildDataZoomConfig();
|
|
157
|
+
chartInstance.setOption({ dataZoom: dataZoomConfig }, false, true);
|
|
158
|
+
} catch (e) {
|
|
159
|
+
console.warn('[useDataZoom] Failed to bind dataZoom:', e);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
isBindingRef.current = false;
|
|
163
|
+
},
|
|
164
|
+
[buildDataZoomConfig]
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// 解绑 dataZoom
|
|
168
|
+
const unbindDataZoom = useCallback((chartInstance: ChartInstance) => {
|
|
169
|
+
if (!chartInstance) return;
|
|
170
|
+
try {
|
|
171
|
+
chartInstance.dispatchAction?.({
|
|
172
|
+
type: 'dataZoom',
|
|
173
|
+
start: 0,
|
|
174
|
+
end: 100,
|
|
175
|
+
});
|
|
176
|
+
} catch (e) {
|
|
177
|
+
console.warn('[useDataZoom] Failed to unbind dataZoom:', e);
|
|
178
|
+
}
|
|
179
|
+
}, []);
|
|
180
|
+
|
|
181
|
+
// 设置缩放范围
|
|
182
|
+
const setZoomRange = useCallback((newStart: number, newEnd: number) => {
|
|
183
|
+
const chart = chartRef.current;
|
|
184
|
+
if (!chart) return;
|
|
185
|
+
|
|
186
|
+
const clampedStart = Math.max(0, Math.min(100, newStart));
|
|
187
|
+
const clampedEnd = Math.max(0, Math.min(100, newEnd));
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
chart.dispatchAction?.({
|
|
191
|
+
type: 'dataZoom',
|
|
192
|
+
start: clampedStart,
|
|
193
|
+
end: clampedEnd,
|
|
194
|
+
});
|
|
195
|
+
} catch (e) {
|
|
196
|
+
console.warn('[useDataZoom] Failed to set zoom range:', e);
|
|
197
|
+
}
|
|
198
|
+
}, []);
|
|
199
|
+
|
|
200
|
+
// 重置缩放
|
|
201
|
+
const resetZoom = useCallback(() => {
|
|
202
|
+
const { start: s, end: e } = configRef.current;
|
|
203
|
+
setZoomRange(s, e);
|
|
204
|
+
}, [setZoomRange]);
|
|
205
|
+
|
|
206
|
+
// 获取当前缩放范围
|
|
207
|
+
const getZoomRange = useCallback((): ZoomRange => {
|
|
208
|
+
const chart = chartRef.current;
|
|
209
|
+
if (!chart) {
|
|
210
|
+
const { start: s, end: e } = configRef.current;
|
|
211
|
+
return { start: s, end: e };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const option = chart.getOption?.();
|
|
216
|
+
const dataZoom = option?.dataZoom as
|
|
217
|
+
| Array<{ type: string; start?: number; end?: number }>
|
|
218
|
+
| undefined;
|
|
219
|
+
if (Array.isArray(dataZoom) && dataZoom.length > 0) {
|
|
220
|
+
const insideZoom = dataZoom.find((dz) => dz.type === 'inside');
|
|
221
|
+
const sliderZoom = dataZoom.find((dz) => dz.type === 'slider');
|
|
222
|
+
const zoom = insideZoom || sliderZoom || dataZoom[0];
|
|
223
|
+
return {
|
|
224
|
+
start: zoom.start ?? configRef.current.start,
|
|
225
|
+
end: zoom.end ?? configRef.current.end,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
} catch (e) {
|
|
229
|
+
console.warn('[useDataZoom] Failed to get zoom range:', e);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return { start: configRef.current.start, end: configRef.current.end };
|
|
233
|
+
}, []);
|
|
234
|
+
|
|
235
|
+
// 绑定事件
|
|
236
|
+
const bindEvents = useCallback((chartInstance: ChartInstance) => {
|
|
237
|
+
if (!chartInstance) return;
|
|
238
|
+
|
|
239
|
+
// 创建节流后的缩放变化处理器(使用稳定的 throttle 值)
|
|
240
|
+
const handleZoomChange: EventHandler = (params: unknown) => {
|
|
241
|
+
// disabled 通过 configRef 读取,保证实时性
|
|
242
|
+
if (configRef.current.disabled) return;
|
|
243
|
+
|
|
244
|
+
const p = params as { start?: number; end?: number } | undefined;
|
|
245
|
+
const { start: newStart, end: newEnd } = p || {};
|
|
246
|
+
if (newStart !== undefined) startValueRef.current = newStart;
|
|
247
|
+
if (newEnd !== undefined) endValueRef.current = newEnd;
|
|
248
|
+
|
|
249
|
+
// 使用 ref 读取 onZoomChange,避免闭包捕获旧值
|
|
250
|
+
onZoomChangeRef.current?.({
|
|
251
|
+
start: newStart ?? configRef.current.start,
|
|
252
|
+
end: newEnd ?? configRef.current.end,
|
|
253
|
+
});
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// 节流包装
|
|
257
|
+
const { throttle: throttleVal } = configRef.current;
|
|
258
|
+
const throttledHandler: EventHandler = (params: unknown) => {
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
if (now - lastCallRef.current >= throttleVal) {
|
|
261
|
+
lastCallRef.current = now;
|
|
262
|
+
handleZoomChange(params);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
throttledHandlerRef.current = throttledHandler;
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
chartInstance.on('datazoom', throttledHandler);
|
|
270
|
+
} catch (e) {
|
|
271
|
+
console.warn('[useDataZoom] Failed to bind events:', e);
|
|
272
|
+
}
|
|
273
|
+
}, []);
|
|
274
|
+
|
|
275
|
+
// 解绑事件
|
|
276
|
+
const unbindEvents = useCallback((chartInstance: ChartInstance) => {
|
|
277
|
+
if (!chartInstance || !throttledHandlerRef.current) return;
|
|
278
|
+
try {
|
|
279
|
+
chartInstance.off('datazoom', throttledHandlerRef.current);
|
|
280
|
+
} catch (e) {
|
|
281
|
+
console.warn('[useDataZoom] Failed to unbind events:', e);
|
|
282
|
+
}
|
|
283
|
+
}, []);
|
|
284
|
+
|
|
285
|
+
// 清理:组件卸载时
|
|
286
|
+
useEffect(() => {
|
|
287
|
+
return () => {
|
|
288
|
+
const chart = chartRef.current;
|
|
289
|
+
if (chart) {
|
|
290
|
+
if (resetOnUnmount) {
|
|
291
|
+
unbindDataZoom(chart);
|
|
292
|
+
}
|
|
293
|
+
unbindEvents(chart);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}, [resetOnUnmount, unbindDataZoom, unbindEvents]);
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
bindDataZoom,
|
|
300
|
+
setZoomRange,
|
|
301
|
+
resetZoom,
|
|
302
|
+
getZoomRange,
|
|
303
|
+
startValue: startValueRef,
|
|
304
|
+
endValue: endValueRef,
|
|
305
|
+
bindEvents,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ============================================================================
|
|
310
|
+
// 类型导出
|
|
311
|
+
// ============================================================================
|
|
312
|
+
|
|
313
|
+
export type {
|
|
314
|
+
UseDataZoomOptions,
|
|
315
|
+
UseDataZoomReturn,
|
|
316
|
+
DataZoomType,
|
|
317
|
+
ZoomRange,
|
|
318
|
+
} from '../../../hooks/types';
|
|
319
|
+
|
|
320
|
+
// ============================================================================
|
|
321
|
+
// 导出
|
|
322
|
+
// ============================================================================
|
|
323
|
+
|
|
324
|
+
// useDataZoom 已在函数声明时导出
|
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
* 提供实时性能指标监控和报告功能
|
|
4
4
|
*/
|
|
5
5
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
6
|
-
import { PerformanceAnalyzer, PerformanceMetricType, PerformanceMetric } from '../core/utils/performance';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* 性能监控配置
|
|
10
9
|
*/
|
|
11
|
-
export interface
|
|
10
|
+
export interface UseFpsMonitorOptions {
|
|
12
11
|
/** 是否启用监控 */
|
|
13
12
|
enabled?: boolean;
|
|
14
13
|
/** 采样间隔 (ms) */
|
|
@@ -43,10 +42,20 @@ export interface PerformanceState {
|
|
|
43
42
|
isMonitoring: boolean;
|
|
44
43
|
}
|
|
45
44
|
|
|
45
|
+
/**
|
|
46
|
+
* 性能报告条目
|
|
47
|
+
*/
|
|
48
|
+
export interface PerformanceReportEntry {
|
|
49
|
+
type: string;
|
|
50
|
+
value: number;
|
|
51
|
+
unit: string;
|
|
52
|
+
timestamp: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
/**
|
|
47
56
|
* 性能监控返回值
|
|
48
57
|
*/
|
|
49
|
-
export interface
|
|
58
|
+
export interface UseFpsMonitorReturn {
|
|
50
59
|
/** 性能状态 */
|
|
51
60
|
state: PerformanceState;
|
|
52
61
|
/** 开始监控 */
|
|
@@ -56,7 +65,7 @@ export interface UsePerformanceReturn {
|
|
|
56
65
|
/** 重置统计数据 */
|
|
57
66
|
reset: () => void;
|
|
58
67
|
/** 获取性能报告 */
|
|
59
|
-
getReport: () =>
|
|
68
|
+
getReport: () => PerformanceReportEntry[];
|
|
60
69
|
/** FPS 告警回调 */
|
|
61
70
|
onFpsWarning?: (fps: number) => void;
|
|
62
71
|
}
|
|
@@ -66,7 +75,13 @@ export interface UsePerformanceReturn {
|
|
|
66
75
|
* @param options 配置选项
|
|
67
76
|
* @returns 性能监控接口
|
|
68
77
|
*/
|
|
69
|
-
|
|
78
|
+
/** @deprecated Use `UseFpsMonitorOptions` instead */
|
|
79
|
+
export type UsePerformanceOptions = UseFpsMonitorOptions;
|
|
80
|
+
|
|
81
|
+
/** @deprecated Use `UseFpsMonitorReturn` instead */
|
|
82
|
+
export type UsePerformanceReturn = UseFpsMonitorReturn;
|
|
83
|
+
|
|
84
|
+
export function useFpsMonitor(options: UseFpsMonitorOptions = {}): UseFpsMonitorReturn {
|
|
70
85
|
const {
|
|
71
86
|
enabled = true,
|
|
72
87
|
sampleInterval = 1000,
|
|
@@ -77,11 +92,14 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
77
92
|
} = options;
|
|
78
93
|
|
|
79
94
|
// Refs
|
|
80
|
-
const analyzerRef = useRef<PerformanceAnalyzer | null>(null);
|
|
81
95
|
const fpsHistoryRef = useRef<number[]>([]);
|
|
82
96
|
const animationFrameRef = useRef<number | null>(null);
|
|
83
97
|
const lastFrameTimeRef = useRef<number>(0);
|
|
84
|
-
const fpsAccumulatorRef = useRef<{ frames: number; lastTime: number }>({
|
|
98
|
+
const fpsAccumulatorRef = useRef<{ frames: number; lastTime: number }>({
|
|
99
|
+
frames: 0,
|
|
100
|
+
lastTime: 0,
|
|
101
|
+
});
|
|
102
|
+
const reportHistoryRef = useRef<PerformanceReportEntry[]>([]);
|
|
85
103
|
|
|
86
104
|
// State
|
|
87
105
|
const [state, setState] = useState<PerformanceState>({
|
|
@@ -121,7 +139,9 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
121
139
|
|
|
122
140
|
// 每秒计算一次 FPS
|
|
123
141
|
if (now - fpsAccumulatorRef.current.lastTime >= 1000) {
|
|
124
|
-
const fps = Math.round(
|
|
142
|
+
const fps = Math.round(
|
|
143
|
+
(fpsAccumulatorRef.current.frames * 1000) / (now - fpsAccumulatorRef.current.lastTime)
|
|
144
|
+
);
|
|
125
145
|
const history = fpsHistoryRef.current;
|
|
126
146
|
|
|
127
147
|
// 更新历史
|
|
@@ -135,7 +155,7 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
135
155
|
const minFps = Math.min(...history);
|
|
136
156
|
const maxFps = Math.max(...history);
|
|
137
157
|
|
|
138
|
-
setState(prev => ({
|
|
158
|
+
setState((prev) => ({
|
|
139
159
|
...prev,
|
|
140
160
|
fps,
|
|
141
161
|
avgFps,
|
|
@@ -145,6 +165,14 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
145
165
|
fpsHistory: [...history],
|
|
146
166
|
}));
|
|
147
167
|
|
|
168
|
+
// 记录性能报告
|
|
169
|
+
reportHistoryRef.current.push({
|
|
170
|
+
type: 'fps',
|
|
171
|
+
value: fps,
|
|
172
|
+
unit: 'fps',
|
|
173
|
+
timestamp: now,
|
|
174
|
+
});
|
|
175
|
+
|
|
148
176
|
// 重置计数器
|
|
149
177
|
fpsAccumulatorRef.current.frames = 0;
|
|
150
178
|
fpsAccumulatorRef.current.lastTime = now;
|
|
@@ -163,27 +191,17 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
163
191
|
// Prevent starting multiple RAF loops
|
|
164
192
|
if (animationFrameRef.current !== null) return;
|
|
165
193
|
|
|
166
|
-
// 初始化分析器
|
|
167
|
-
if (!analyzerRef.current) {
|
|
168
|
-
analyzerRef.current = PerformanceAnalyzer.getInstance({
|
|
169
|
-
enabled: true,
|
|
170
|
-
sampleInterval,
|
|
171
|
-
autoStart: false,
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
analyzerRef.current.start();
|
|
176
|
-
|
|
177
194
|
// 重置 FPS 计算
|
|
178
195
|
fpsAccumulatorRef.current = { frames: 0, lastTime: performance.now() };
|
|
179
196
|
lastFrameTimeRef.current = performance.now();
|
|
180
197
|
fpsHistoryRef.current = [];
|
|
198
|
+
reportHistoryRef.current = [];
|
|
181
199
|
|
|
182
200
|
// 开始 FPS 监控循环
|
|
183
201
|
animationFrameRef.current = requestAnimationFrame(updateFps);
|
|
184
202
|
|
|
185
|
-
setState(prev => ({ ...prev, isMonitoring: true }));
|
|
186
|
-
}, [enabled,
|
|
203
|
+
setState((prev) => ({ ...prev, isMonitoring: true }));
|
|
204
|
+
}, [enabled, updateFps]);
|
|
187
205
|
|
|
188
206
|
/**
|
|
189
207
|
* 停止监控
|
|
@@ -195,19 +213,16 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
195
213
|
animationFrameRef.current = null;
|
|
196
214
|
}
|
|
197
215
|
|
|
198
|
-
|
|
199
|
-
analyzerRef.current?.stop();
|
|
200
|
-
|
|
201
|
-
setState(prev => ({ ...prev, isMonitoring: false }));
|
|
216
|
+
setState((prev) => ({ ...prev, isMonitoring: false }));
|
|
202
217
|
}, []);
|
|
203
218
|
|
|
204
219
|
/**
|
|
205
220
|
* 重置统计数据
|
|
206
221
|
*/
|
|
207
222
|
const reset = useCallback(() => {
|
|
208
|
-
PerformanceAnalyzer.resetInstance();
|
|
209
223
|
fpsHistoryRef.current = [];
|
|
210
|
-
|
|
224
|
+
reportHistoryRef.current = [];
|
|
225
|
+
setState((prev) => ({
|
|
211
226
|
...prev,
|
|
212
227
|
fps: 60,
|
|
213
228
|
avgFps: 60,
|
|
@@ -221,16 +236,8 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
221
236
|
/**
|
|
222
237
|
* 获取性能报告
|
|
223
238
|
*/
|
|
224
|
-
const getReport = useCallback(():
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
try {
|
|
228
|
-
const report = analyzerRef.current.getAllMetrics?.();
|
|
229
|
-
if (!report) return [];
|
|
230
|
-
return Array.from(report.values()).flat();
|
|
231
|
-
} catch {
|
|
232
|
-
return [];
|
|
233
|
-
}
|
|
239
|
+
const getReport = useCallback((): PerformanceReportEntry[] => {
|
|
240
|
+
return reportHistoryRef.current.slice(-100); // 返回最近 100 条记录
|
|
234
241
|
}, []);
|
|
235
242
|
|
|
236
243
|
// 自动启动
|
|
@@ -254,10 +261,10 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
254
261
|
}
|
|
255
262
|
|
|
256
263
|
/**
|
|
257
|
-
* 轻量级 FPS
|
|
264
|
+
* 轻量级 FPS 计数器 Hook
|
|
258
265
|
* 用于实时显示图表 FPS
|
|
259
266
|
*/
|
|
260
|
-
export function
|
|
267
|
+
export function useFpsCounter(): number {
|
|
261
268
|
const [fps, setFps] = useState(60);
|
|
262
269
|
const frameCountRef = useRef(0);
|
|
263
270
|
const lastTimeRef = useRef(performance.now());
|
|
@@ -288,4 +295,5 @@ export function useFpsMonitor(): number {
|
|
|
288
295
|
return fps;
|
|
289
296
|
}
|
|
290
297
|
|
|
291
|
-
|
|
298
|
+
/** @deprecated Use `useFpsMonitor` instead */
|
|
299
|
+
export const usePerformance = useFpsMonitor;
|