@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.
Files changed (164) hide show
  1. package/CHANGELOG.md +245 -0
  2. package/README.md +104 -302
  3. package/dist/cjs/index.js +1 -1
  4. package/dist/cjs/vendors.js +1 -0
  5. package/dist/cjs/vendors~echarts.js +1 -0
  6. package/dist/esm/index.js +1 -58151
  7. package/dist/esm/vendors.js +1 -0
  8. package/dist/esm/vendors~echarts.js +1 -0
  9. package/package.json +19 -25
  10. package/src/adapters/MiniAppAdapter.ts +136 -0
  11. package/src/adapters/__tests__/index.test.ts +1 -1
  12. package/src/adapters/h5/__tests__/index.test.ts +4 -2
  13. package/src/adapters/h5/index.ts +63 -64
  14. package/src/adapters/harmony/index.ts +23 -245
  15. package/src/adapters/index.ts +49 -45
  16. package/src/adapters/swan/index.ts +6 -69
  17. package/src/adapters/tt/index.ts +7 -70
  18. package/src/adapters/types.ts +25 -58
  19. package/src/adapters/weapp/index.ts +6 -69
  20. package/src/charts/__tests__/testUtils.tsx +87 -0
  21. package/src/charts/boxplot/__tests__/index.test.tsx +49 -103
  22. package/src/charts/boxplot/index.tsx +2 -1
  23. package/src/charts/boxplot/types.ts +17 -16
  24. package/src/charts/common/BaseChartWrapper.tsx +90 -82
  25. package/src/charts/common/__mocks__/BaseChartWrapper.tsx +17 -0
  26. package/src/charts/createChartComponent.tsx +36 -0
  27. package/src/charts/createOptionChartComponent.tsx +32 -0
  28. package/src/charts/funnel/__tests__/index.test.tsx +99 -0
  29. package/src/charts/funnel/index.tsx +60 -10
  30. package/src/charts/funnel/types.ts +6 -0
  31. package/src/charts/graph/__tests__/index.test.tsx +102 -33
  32. package/src/charts/graph/index.tsx +66 -9
  33. package/src/charts/graph/types.ts +6 -0
  34. package/src/charts/heatmap/__tests__/index.test.tsx +139 -0
  35. package/src/charts/heatmap/index.tsx +103 -10
  36. package/src/charts/heatmap/types.ts +6 -0
  37. package/src/charts/index.ts +74 -26
  38. package/src/charts/liquid/__tests__/index.test.tsx +52 -0
  39. package/src/charts/liquid/index.tsx +239 -182
  40. package/src/charts/liquid/types.ts +11 -11
  41. package/src/charts/parallel/__tests__/index.test.tsx +40 -67
  42. package/src/charts/parallel/index.tsx +2 -1
  43. package/src/charts/parallel/types.ts +19 -18
  44. package/src/charts/radar/__tests__/index.test.tsx +210 -0
  45. package/src/charts/radar/index.tsx +143 -10
  46. package/src/charts/radar/types.ts +13 -0
  47. package/src/charts/sankey/__tests__/index.test.tsx +124 -0
  48. package/src/charts/sankey/index.tsx +62 -10
  49. package/src/charts/sankey/types.ts +6 -0
  50. package/src/charts/tree/__tests__/index.test.tsx +71 -0
  51. package/src/charts/tree/index.tsx +5 -2
  52. package/src/charts/tree/types.ts +9 -9
  53. package/src/charts/types.ts +208 -106
  54. package/src/charts/utils.ts +9 -7
  55. package/src/charts/wordcloud/__tests__/index.test.tsx +98 -31
  56. package/src/charts/wordcloud/index.tsx +75 -9
  57. package/src/charts/wordcloud/types.ts +6 -0
  58. package/src/components/DataFilter/index.tsx +32 -10
  59. package/src/core/animation/types.ts +6 -6
  60. package/src/core/components/Annotation.tsx +6 -7
  61. package/src/core/components/BaseChart.tsx +110 -168
  62. package/src/core/components/ErrorBoundary.tsx +17 -4
  63. package/src/core/components/LazyChart.tsx +54 -55
  64. package/src/core/components/hooks/index.ts +6 -2
  65. package/src/core/components/hooks/useChartInit.ts +6 -3
  66. package/src/core/components/hooks/usePerformance.ts +8 -2
  67. package/src/core/components/hooks/useVirtualScroll.ts +2 -1
  68. package/src/core/index.ts +1 -1
  69. package/src/core/themes/ThemeManager.ts +1 -1
  70. package/src/core/types/common.ts +2 -1
  71. package/src/core/types/index.ts +0 -12
  72. package/src/core/types/platform.ts +3 -5
  73. package/src/core/utils/__tests__/deepClone.test.ts +317 -0
  74. package/src/core/utils/__tests__/index.test.ts +2 -1
  75. package/src/core/utils/chartInstances.ts +13 -0
  76. package/src/core/utils/common.ts +20 -29
  77. package/src/core/utils/deepClone.ts +114 -0
  78. package/src/core/utils/download.ts +128 -0
  79. package/src/core/utils/drillDown.ts +34 -353
  80. package/src/core/utils/drillDownHelpers.ts +426 -0
  81. package/src/core/utils/events.ts +12 -0
  82. package/src/core/utils/export/ExportUtils.ts +36 -67
  83. package/src/core/utils/format.ts +44 -0
  84. package/src/core/utils/index.ts +21 -154
  85. package/src/core/utils/merge.ts +25 -0
  86. package/src/core/utils/performance/PerformanceAnalyzer.ts +38 -21
  87. package/src/core/utils/performance/hooks.ts +7 -0
  88. package/src/core/utils/performance/index.ts +2 -0
  89. package/src/{hooks → core/utils/performance}/useAnimation.ts +45 -41
  90. package/src/core/utils/performance/useDataZoom.ts +324 -0
  91. package/src/{hooks → core/utils/performance}/usePerformance.ts +49 -41
  92. package/src/core/utils/performance/usePerformanceHooks.ts +278 -0
  93. package/src/core/utils/performanceUtils.ts +310 -0
  94. package/src/core/utils/runtime.ts +190 -0
  95. package/src/core/utils/setOptionUtils.ts +59 -0
  96. package/src/core/version.ts +14 -0
  97. package/src/editor/EnhancedThemeEditor.tsx +362 -540
  98. package/src/editor/ThemeEditor.tsx +55 -321
  99. package/src/editor/components/ThemeBasicSettings.tsx +113 -0
  100. package/src/editor/components/ThemeColorEditor.tsx +105 -0
  101. package/src/editor/components/ThemeSelector.tsx +70 -0
  102. package/src/editor/hooks/useThemeEditorState.ts +201 -0
  103. package/src/editor/index.ts +10 -2
  104. package/src/hooks/__tests__/index.test.tsx +3 -1
  105. package/src/hooks/chartConnectHelpers.ts +341 -0
  106. package/src/hooks/index.ts +55 -660
  107. package/src/hooks/types.ts +189 -0
  108. package/src/hooks/useChartAutoResize.ts +73 -0
  109. package/src/hooks/useChartConnect.ts +92 -238
  110. package/src/hooks/useChartDownload.ts +25 -27
  111. package/src/hooks/useChartHistory.ts +34 -49
  112. package/src/hooks/useChartInit.ts +59 -0
  113. package/src/hooks/useChartOptions.ts +259 -0
  114. package/src/hooks/useChartPerformance.ts +109 -0
  115. package/src/hooks/useChartSelection.ts +52 -49
  116. package/src/hooks/useChartTheme.ts +51 -0
  117. package/src/hooks/useDataTransform.ts +19 -4
  118. package/src/hooks/utils/chartDownloadUtils.ts +40 -53
  119. package/src/hooks/utils/dataTransformUtils.ts +22 -0
  120. package/src/index.ts +48 -34
  121. package/src/main.tsx +4 -9
  122. package/src/react-dom.d.ts +3 -3
  123. package/src/themes/index.ts +30 -855
  124. package/src/themes/palettes/blue-green.ts +13 -0
  125. package/src/themes/palettes/chalk.ts +13 -0
  126. package/src/themes/palettes/cyber.ts +44 -0
  127. package/src/themes/palettes/dark.ts +52 -0
  128. package/src/themes/palettes/default.ts +52 -0
  129. package/src/themes/palettes/elegant.ts +34 -0
  130. package/src/themes/palettes/forest.ts +13 -0
  131. package/src/themes/palettes/glass.ts +49 -0
  132. package/src/themes/palettes/golden.ts +13 -0
  133. package/src/themes/palettes/neon.ts +43 -0
  134. package/src/themes/palettes/ocean.ts +39 -0
  135. package/src/themes/palettes/pastel.ts +37 -0
  136. package/src/themes/palettes/purple-passion.ts +13 -0
  137. package/src/themes/palettes/retro.ts +33 -0
  138. package/src/themes/palettes/sunset.ts +40 -0
  139. package/src/themes/palettes/walden.ts +13 -0
  140. package/src/themes/registry.ts +184 -0
  141. package/src/themes/types.ts +213 -0
  142. package/src/charts/bar/__tests__/index.test.tsx +0 -113
  143. package/src/charts/bar/index.tsx +0 -14
  144. package/src/charts/candlestick/__tests__/index.test.tsx +0 -40
  145. package/src/charts/candlestick/index.tsx +0 -13
  146. package/src/charts/gauge/index.tsx +0 -14
  147. package/src/charts/line/__tests__/index.test.tsx +0 -107
  148. package/src/charts/line/index.tsx +0 -15
  149. package/src/charts/pie/__tests__/index.test.tsx +0 -112
  150. package/src/charts/pie/index.tsx +0 -14
  151. package/src/charts/scatter/index.tsx +0 -14
  152. package/src/charts/sunburst/index.tsx +0 -18
  153. package/src/charts/treemap/index.tsx +0 -18
  154. package/src/core/utils/codeGenerator/CodeGenerator.ts +0 -669
  155. package/src/core/utils/codeGenerator/index.ts +0 -13
  156. package/src/core/utils/codeGenerator/types.ts +0 -198
  157. package/src/core/utils/configGenerator/ConfigGenerator.ts +0 -583
  158. package/src/core/utils/configGenerator/index.ts +0 -13
  159. package/src/core/utils/configGenerator/types.ts +0 -445
  160. package/src/core/utils/debug/DebugPanel.tsx +0 -637
  161. package/src/core/utils/debug/debugger.ts +0 -322
  162. package/src/core/utils/debug/index.ts +0 -21
  163. package/src/core/utils/debug/types.ts +0 -142
  164. 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 UsePerformanceOptions {
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 UsePerformanceReturn {
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: () => PerformanceMetric[];
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
- export function usePerformance(options: UsePerformanceOptions = {}): UsePerformanceReturn {
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 }>({ frames: 0, lastTime: 0 });
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((fpsAccumulatorRef.current.frames * 1000) / (now - fpsAccumulatorRef.current.lastTime));
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, sampleInterval, updateFps]);
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
- setState(prev => ({
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((): PerformanceMetric[] => {
225
- if (!analyzerRef.current) return [];
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 监控 Hook
264
+ * 轻量级 FPS 计数器 Hook
258
265
  * 用于实时显示图表 FPS
259
266
  */
260
- export function useFpsMonitor(): number {
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
- export default usePerformance;
298
+ /** @deprecated Use `useFpsMonitor` instead */
299
+ export const usePerformance = useFpsMonitor;