@agions/taroviz 1.11.5 → 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 +31 -46
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/vendors.js +1 -1
- package/dist/cjs/vendors~echarts.js +1 -1
- package/dist/esm/index.js +1 -14270
- package/dist/esm/vendors.js +1 -16770
- package/dist/esm/vendors~echarts.js +1 -59417
- package/package.json +10 -15
- package/src/adapters/h5/index.ts +38 -38
- package/src/adapters/index.ts +32 -34
- package/src/adapters/types.ts +23 -55
- package/src/charts/boxplot/types.ts +2 -2
- package/src/charts/common/BaseChartWrapper.tsx +9 -7
- package/src/charts/createChartComponent.tsx +9 -21
- package/src/charts/createOptionChartComponent.tsx +32 -0
- package/src/charts/funnel/__tests__/index.test.tsx +99 -0
- package/src/charts/funnel/index.tsx +64 -0
- package/src/charts/funnel/types.ts +6 -0
- package/src/charts/graph/__tests__/index.test.tsx +116 -0
- package/src/charts/graph/index.tsx +70 -0
- package/src/charts/graph/types.ts +6 -0
- package/src/charts/heatmap/__tests__/index.test.tsx +139 -0
- package/src/charts/heatmap/index.tsx +107 -0
- package/src/charts/heatmap/types.ts +6 -0
- package/src/charts/index.ts +47 -57
- package/src/charts/liquid/__tests__/index.test.tsx +52 -0
- package/src/charts/liquid/index.tsx +7 -133
- package/src/charts/liquid/types.ts +6 -6
- package/src/charts/parallel/types.ts +3 -3
- package/src/charts/radar/__tests__/index.test.tsx +210 -0
- package/src/charts/radar/index.tsx +147 -0
- package/src/charts/radar/types.ts +13 -0
- package/src/charts/sankey/__tests__/index.test.tsx +124 -0
- package/src/charts/sankey/index.tsx +70 -0
- package/src/charts/sankey/types.ts +6 -0
- package/src/charts/tree/__tests__/index.test.tsx +71 -0
- package/src/charts/tree/index.tsx +1 -1
- package/src/charts/tree/types.ts +8 -8
- package/src/charts/types.ts +208 -106
- package/src/charts/wordcloud/__tests__/index.test.tsx +106 -0
- package/src/charts/wordcloud/index.tsx +79 -0
- package/src/charts/wordcloud/types.ts +6 -0
- package/src/components/DataFilter/index.tsx +7 -6
- package/src/core/animation/types.ts +6 -6
- package/src/core/components/Annotation.tsx +6 -6
- package/src/core/components/BaseChart.tsx +97 -133
- package/src/core/components/LazyChart.tsx +3 -8
- package/src/core/components/hooks/index.ts +6 -2
- package/src/core/components/hooks/usePerformance.ts +8 -2
- package/src/core/components/hooks/useVirtualScroll.ts +2 -1
- package/src/core/types/common.ts +2 -1
- package/src/core/types/platform.ts +1 -0
- 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 -36
- package/src/core/utils/deepClone.ts +114 -0
- package/src/core/utils/download.ts +22 -28
- package/src/core/utils/drillDown.ts +1 -0
- package/src/core/utils/events.ts +12 -0
- package/src/core/utils/export/ExportUtils.ts +2 -1
- package/src/core/utils/format.ts +44 -0
- package/src/core/utils/index.ts +18 -159
- package/src/core/utils/merge.ts +25 -0
- package/src/core/utils/performance/PerformanceAnalyzer.ts +3 -1
- 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 +6 -5
- package/src/{hooks → core/utils/performance}/useDataZoom.ts +7 -2
- package/src/{hooks → core/utils/performance}/usePerformance.ts +39 -39
- package/src/{hooks → core/utils/performance}/usePerformanceHooks.ts +39 -39
- package/src/core/utils/runtime.ts +190 -0
- package/src/editor/components/ThemeSelector.tsx +3 -3
- package/src/hooks/chartConnectHelpers.ts +6 -0
- package/src/hooks/index.ts +54 -626
- package/src/hooks/types.ts +27 -0
- package/src/hooks/useChartAutoResize.ts +73 -0
- package/src/hooks/useChartConnect.ts +5 -1
- package/src/hooks/useChartDownload.ts +1 -1
- package/src/hooks/useChartHistory.ts +1 -3
- 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 +23 -12
- package/src/hooks/useChartTheme.ts +51 -0
- package/src/hooks/useDataTransform.ts +19 -4
- package/src/index.ts +5 -10
- 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/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 -449
- package/src/core/utils/debug/DebugPanel.tsx +0 -640
- 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/core/utils/index.ts
CHANGED
|
@@ -1,170 +1,29 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
click: 'click',
|
|
4
|
-
mousemove: 'mousemove',
|
|
5
|
-
mouseup: 'mouseup',
|
|
6
|
-
mousedown: 'mousedown',
|
|
7
|
-
mouseover: 'mouseover',
|
|
8
|
-
mouseout: 'mouseout',
|
|
9
|
-
globalout: 'globalout',
|
|
10
|
-
};
|
|
1
|
+
// Re-export barrel file — all public APIs preserved
|
|
2
|
+
// External import paths remain unchanged
|
|
11
3
|
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
import { uuid, shortId, prefixedId } from './uuid';
|
|
15
|
-
|
|
16
|
-
// 导入国际化工具
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* 深度合并对象
|
|
20
|
-
* @param target 目标对象
|
|
21
|
-
* @param source 源对象
|
|
22
|
-
* @returns 合并后的对象
|
|
23
|
-
*/
|
|
24
|
-
export function deepMerge<T extends Record<string, unknown>>(
|
|
25
|
-
target: T,
|
|
26
|
-
source: Partial<Record<string, unknown>>
|
|
27
|
-
): T {
|
|
28
|
-
const result: Record<string, unknown> = { ...target };
|
|
29
|
-
|
|
30
|
-
Object.keys(source).forEach((key) => {
|
|
31
|
-
if (source[key] instanceof Object && key in target && target[key] instanceof Object) {
|
|
32
|
-
result[key] = deepMerge(
|
|
33
|
-
target[key] as Record<string, unknown>,
|
|
34
|
-
source[key] as Record<string, unknown>
|
|
35
|
-
);
|
|
36
|
-
} else {
|
|
37
|
-
result[key] = source[key];
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
return result as T;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* 防抖函数
|
|
46
|
-
* @param fn 需要防抖的函数
|
|
47
|
-
* @param delay 延迟时间(毫秒)
|
|
48
|
-
* @returns 防抖后的函数
|
|
49
|
-
*/
|
|
50
|
-
export function debounce<T extends (...args: unknown[]) => unknown>(
|
|
51
|
-
fn: T,
|
|
52
|
-
delay: number
|
|
53
|
-
): (...args: Parameters<T>) => void {
|
|
54
|
-
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
55
|
-
|
|
56
|
-
return function (this: unknown, ...args: Parameters<T>): void {
|
|
57
|
-
if (timer) {
|
|
58
|
-
clearTimeout(timer);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
timer = setTimeout(() => {
|
|
62
|
-
fn.call(this, ...args);
|
|
63
|
-
timer = null;
|
|
64
|
-
}, delay);
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* 节流函数
|
|
70
|
-
* @param fn 需要节流的函数
|
|
71
|
-
* @param interval 间隔时间(毫秒)
|
|
72
|
-
* @returns 节流后的函数
|
|
73
|
-
*/
|
|
74
|
-
export function throttle<T extends (...args: unknown[]) => unknown>(
|
|
75
|
-
fn: T,
|
|
76
|
-
interval: number
|
|
77
|
-
): (...args: Parameters<T>) => void {
|
|
78
|
-
let lastTime = 0;
|
|
4
|
+
// 事件常量
|
|
5
|
+
export { events } from './events';
|
|
79
6
|
|
|
80
|
-
|
|
81
|
-
|
|
7
|
+
// 对象合并
|
|
8
|
+
export { deepMerge } from './merge';
|
|
82
9
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
lastTime = now;
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
}
|
|
10
|
+
// 格式化与颜色工具
|
|
11
|
+
export { formatNumber, getContrastColor } from './format';
|
|
89
12
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
* @returns 环境信息
|
|
93
|
-
*/
|
|
94
|
-
export function getEnvironment() {
|
|
95
|
-
const isServer = typeof window === 'undefined';
|
|
96
|
-
const isClient = !isServer;
|
|
13
|
+
// 检测当前环境(简易版,向后兼容)
|
|
14
|
+
export { getEnvironment } from './runtime';
|
|
97
15
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
typeof win.wx !== 'undefined' &&
|
|
102
|
-
typeof (win.wx as { getSystemInfoSync?: unknown })?.getSystemInfoSync === 'function';
|
|
103
|
-
const isAlipay =
|
|
104
|
-
typeof win.my !== 'undefined' &&
|
|
105
|
-
typeof (win.my as { getSystemInfoSync?: unknown })?.getSystemInfoSync === 'function';
|
|
106
|
-
const isWeb = isClient && !isWeapp && !isAlipay;
|
|
16
|
+
// 统一运行时检测
|
|
17
|
+
export { detectRuntime, resetRuntimeCache } from './runtime';
|
|
18
|
+
export type { RuntimeInfo, MiniAppType } from './runtime';
|
|
107
19
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
isClient,
|
|
111
|
-
isWeapp,
|
|
112
|
-
isAlipay,
|
|
113
|
-
isWeb,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
20
|
+
// UUID工具函数
|
|
21
|
+
export { uuid, shortId, prefixedId } from './uuid';
|
|
116
22
|
|
|
117
|
-
|
|
118
|
-
*
|
|
119
|
-
* @param value 要格式化的数值
|
|
120
|
-
* @param digits 小数位数
|
|
121
|
-
* @param options 配置选项
|
|
122
|
-
* @returns 格式化后的字符串
|
|
123
|
-
*/
|
|
124
|
-
export function formatNumber(
|
|
125
|
-
value: number,
|
|
126
|
-
digits: number = 2,
|
|
127
|
-
options: {
|
|
128
|
-
useGrouping?: boolean;
|
|
129
|
-
locale?: string;
|
|
130
|
-
} = {}
|
|
131
|
-
): string {
|
|
132
|
-
const { useGrouping = true, locale = 'zh-CN' } = options;
|
|
133
|
-
|
|
134
|
-
return new Intl.NumberFormat(locale, {
|
|
135
|
-
minimumFractionDigits: digits,
|
|
136
|
-
maximumFractionDigits: digits,
|
|
137
|
-
useGrouping,
|
|
138
|
-
}).format(value);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* 获取颜色的对比色
|
|
143
|
-
* @param color 十六进制颜色值
|
|
144
|
-
* @returns 对比色
|
|
145
|
-
*/
|
|
146
|
-
export function getContrastColor(color: string): string {
|
|
147
|
-
// 移除#前缀
|
|
148
|
-
const hex = color.replace('#', '');
|
|
149
|
-
|
|
150
|
-
// 转换为RGB
|
|
151
|
-
const r = parseInt(hex.substring(0, 2), 16);
|
|
152
|
-
const g = parseInt(hex.substring(2, 4), 16);
|
|
153
|
-
const b = parseInt(hex.substring(4, 6), 16);
|
|
154
|
-
|
|
155
|
-
// 计算亮度
|
|
156
|
-
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
|
157
|
-
|
|
158
|
-
// 根据亮度返回黑色或白色
|
|
159
|
-
return brightness > 128 ? '#000000' : '#FFFFFF';
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// 导出UUID工具函数
|
|
163
|
-
export { uuid, shortId, prefixedId };
|
|
164
|
-
|
|
165
|
-
// 导出国际化工具
|
|
23
|
+
// 国际化工具
|
|
24
|
+
import * as i18n from './i18n';
|
|
166
25
|
export { i18n };
|
|
167
26
|
|
|
168
|
-
//
|
|
27
|
+
// 性能优化工具(含 debounce, throttle, DebounceManager 等)
|
|
169
28
|
export * from './performanceUtils';
|
|
170
29
|
export { DebounceManager } from './performanceUtils';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 深度合并对象
|
|
3
|
+
* @param target 目标对象
|
|
4
|
+
* @param source 源对象
|
|
5
|
+
* @returns 合并后的对象
|
|
6
|
+
*/
|
|
7
|
+
export function deepMerge<T extends Record<string, unknown>>(
|
|
8
|
+
target: T,
|
|
9
|
+
source: Partial<Record<string, unknown>>
|
|
10
|
+
): T {
|
|
11
|
+
const result: Record<string, unknown> = { ...target };
|
|
12
|
+
|
|
13
|
+
Object.keys(source).forEach((key) => {
|
|
14
|
+
if (source[key] instanceof Object && key in target && target[key] instanceof Object) {
|
|
15
|
+
result[key] = deepMerge(
|
|
16
|
+
target[key] as Record<string, unknown>,
|
|
17
|
+
source[key] as Record<string, unknown>
|
|
18
|
+
);
|
|
19
|
+
} else {
|
|
20
|
+
result[key] = source[key];
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return result as T;
|
|
25
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
1
2
|
/**
|
|
2
3
|
* TaroViz 性能分析工具核心实现
|
|
3
4
|
*/
|
|
@@ -169,7 +170,7 @@ export class PerformanceAnalyzer {
|
|
|
169
170
|
|
|
170
171
|
this.isMonitoring = true;
|
|
171
172
|
this.startTime = Date.now();
|
|
172
|
-
this.lastFrameTime = performance.now();
|
|
173
|
+
this.lastFrameTime = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
173
174
|
|
|
174
175
|
// 启动采样定时器
|
|
175
176
|
if (this.config.sampleInterval && this.config.realTime) {
|
|
@@ -231,6 +232,7 @@ export class PerformanceAnalyzer {
|
|
|
231
232
|
* 开始帧率监控
|
|
232
233
|
*/
|
|
233
234
|
private startFrameRateMonitoring(): void {
|
|
235
|
+
if (typeof requestAnimationFrame === 'undefined' || typeof performance === 'undefined') return;
|
|
234
236
|
const updateFrameRate = () => {
|
|
235
237
|
if (!this.isMonitoring) {
|
|
236
238
|
return;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
1
2
|
/**
|
|
2
3
|
* useAnimation - 图表动画控制 Hook
|
|
3
4
|
* 提供图表动画的播放、暂停、控制等功能
|
|
4
5
|
*/
|
|
5
6
|
import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
|
|
6
|
-
import type { ChartInstance } from '
|
|
7
|
+
import type { ChartInstance } from '../../../hooks/types';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* 动画状态
|
|
@@ -47,11 +48,11 @@ export interface UseAnimationReturn {
|
|
|
47
48
|
/** 停止动画并重置 */
|
|
48
49
|
stop: () => void;
|
|
49
50
|
/** 跳转到指定帧 */
|
|
50
|
-
seekTo: (
|
|
51
|
+
seekTo: (_frame: number) => void;
|
|
51
52
|
/** 跳转到指定进度 */
|
|
52
|
-
seekToProgress: (
|
|
53
|
+
seekToProgress: (_progress: number) => void;
|
|
53
54
|
/** 设置播放速度 */
|
|
54
|
-
setPlaybackSpeed: (
|
|
55
|
+
setPlaybackSpeed: (_speed: number) => void;
|
|
55
56
|
/** 播放速度 */
|
|
56
57
|
playbackSpeed: number;
|
|
57
58
|
}
|
|
@@ -248,7 +249,7 @@ export function useAnimation(
|
|
|
248
249
|
anim.currentFrame = clampedFrame;
|
|
249
250
|
setFrame(clampedFrame);
|
|
250
251
|
|
|
251
|
-
const
|
|
252
|
+
const _progress = clampedFrame / currentTotalFrames;
|
|
252
253
|
try {
|
|
253
254
|
if (chart.setOption) {
|
|
254
255
|
chart.setOption({}, false, true);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { useRef, useCallback, useEffect, useMemo } from 'react';
|
|
6
6
|
import type { RefObject } from 'react';
|
|
7
|
-
import type { ChartInstance, DataZoomType, ZoomRange, EventHandler } from '
|
|
7
|
+
import type { ChartInstance, DataZoomType, ZoomRange, EventHandler } from '../../../hooks/types';
|
|
8
8
|
|
|
9
9
|
// ============================================================================
|
|
10
10
|
// Hook 实现
|
|
@@ -310,7 +310,12 @@ export function useDataZoom(
|
|
|
310
310
|
// 类型导出
|
|
311
311
|
// ============================================================================
|
|
312
312
|
|
|
313
|
-
export type {
|
|
313
|
+
export type {
|
|
314
|
+
UseDataZoomOptions,
|
|
315
|
+
UseDataZoomReturn,
|
|
316
|
+
DataZoomType,
|
|
317
|
+
ZoomRange,
|
|
318
|
+
} from '../../../hooks/types';
|
|
314
319
|
|
|
315
320
|
// ============================================================================
|
|
316
321
|
// 导出
|
|
@@ -3,16 +3,11 @@
|
|
|
3
3
|
* 提供实时性能指标监控和报告功能
|
|
4
4
|
*/
|
|
5
5
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
6
|
-
import {
|
|
7
|
-
PerformanceAnalyzer,
|
|
8
|
-
PerformanceMetricType,
|
|
9
|
-
PerformanceMetric,
|
|
10
|
-
} from '../core/utils/performance';
|
|
11
6
|
|
|
12
7
|
/**
|
|
13
8
|
* 性能监控配置
|
|
14
9
|
*/
|
|
15
|
-
export interface
|
|
10
|
+
export interface UseFpsMonitorOptions {
|
|
16
11
|
/** 是否启用监控 */
|
|
17
12
|
enabled?: boolean;
|
|
18
13
|
/** 采样间隔 (ms) */
|
|
@@ -47,10 +42,20 @@ export interface PerformanceState {
|
|
|
47
42
|
isMonitoring: boolean;
|
|
48
43
|
}
|
|
49
44
|
|
|
45
|
+
/**
|
|
46
|
+
* 性能报告条目
|
|
47
|
+
*/
|
|
48
|
+
export interface PerformanceReportEntry {
|
|
49
|
+
type: string;
|
|
50
|
+
value: number;
|
|
51
|
+
unit: string;
|
|
52
|
+
timestamp: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
/**
|
|
51
56
|
* 性能监控返回值
|
|
52
57
|
*/
|
|
53
|
-
export interface
|
|
58
|
+
export interface UseFpsMonitorReturn {
|
|
54
59
|
/** 性能状态 */
|
|
55
60
|
state: PerformanceState;
|
|
56
61
|
/** 开始监控 */
|
|
@@ -60,7 +65,7 @@ export interface UsePerformanceReturn {
|
|
|
60
65
|
/** 重置统计数据 */
|
|
61
66
|
reset: () => void;
|
|
62
67
|
/** 获取性能报告 */
|
|
63
|
-
getReport: () =>
|
|
68
|
+
getReport: () => PerformanceReportEntry[];
|
|
64
69
|
/** FPS 告警回调 */
|
|
65
70
|
onFpsWarning?: (fps: number) => void;
|
|
66
71
|
}
|
|
@@ -70,7 +75,13 @@ export interface UsePerformanceReturn {
|
|
|
70
75
|
* @param options 配置选项
|
|
71
76
|
* @returns 性能监控接口
|
|
72
77
|
*/
|
|
73
|
-
|
|
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 {
|
|
74
85
|
const {
|
|
75
86
|
enabled = true,
|
|
76
87
|
sampleInterval = 1000,
|
|
@@ -81,7 +92,6 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
81
92
|
} = options;
|
|
82
93
|
|
|
83
94
|
// Refs
|
|
84
|
-
const analyzerRef = useRef<PerformanceAnalyzer | null>(null);
|
|
85
95
|
const fpsHistoryRef = useRef<number[]>([]);
|
|
86
96
|
const animationFrameRef = useRef<number | null>(null);
|
|
87
97
|
const lastFrameTimeRef = useRef<number>(0);
|
|
@@ -89,6 +99,7 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
89
99
|
frames: 0,
|
|
90
100
|
lastTime: 0,
|
|
91
101
|
});
|
|
102
|
+
const reportHistoryRef = useRef<PerformanceReportEntry[]>([]);
|
|
92
103
|
|
|
93
104
|
// State
|
|
94
105
|
const [state, setState] = useState<PerformanceState>({
|
|
@@ -154,6 +165,14 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
154
165
|
fpsHistory: [...history],
|
|
155
166
|
}));
|
|
156
167
|
|
|
168
|
+
// 记录性能报告
|
|
169
|
+
reportHistoryRef.current.push({
|
|
170
|
+
type: 'fps',
|
|
171
|
+
value: fps,
|
|
172
|
+
unit: 'fps',
|
|
173
|
+
timestamp: now,
|
|
174
|
+
});
|
|
175
|
+
|
|
157
176
|
// 重置计数器
|
|
158
177
|
fpsAccumulatorRef.current.frames = 0;
|
|
159
178
|
fpsAccumulatorRef.current.lastTime = now;
|
|
@@ -172,27 +191,17 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
172
191
|
// Prevent starting multiple RAF loops
|
|
173
192
|
if (animationFrameRef.current !== null) return;
|
|
174
193
|
|
|
175
|
-
// 初始化分析器
|
|
176
|
-
if (!analyzerRef.current) {
|
|
177
|
-
analyzerRef.current = PerformanceAnalyzer.getInstance({
|
|
178
|
-
enabled: true,
|
|
179
|
-
sampleInterval,
|
|
180
|
-
autoStart: false,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
analyzerRef.current.start();
|
|
185
|
-
|
|
186
194
|
// 重置 FPS 计算
|
|
187
195
|
fpsAccumulatorRef.current = { frames: 0, lastTime: performance.now() };
|
|
188
196
|
lastFrameTimeRef.current = performance.now();
|
|
189
197
|
fpsHistoryRef.current = [];
|
|
198
|
+
reportHistoryRef.current = [];
|
|
190
199
|
|
|
191
200
|
// 开始 FPS 监控循环
|
|
192
201
|
animationFrameRef.current = requestAnimationFrame(updateFps);
|
|
193
202
|
|
|
194
203
|
setState((prev) => ({ ...prev, isMonitoring: true }));
|
|
195
|
-
}, [enabled,
|
|
204
|
+
}, [enabled, updateFps]);
|
|
196
205
|
|
|
197
206
|
/**
|
|
198
207
|
* 停止监控
|
|
@@ -204,9 +213,6 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
204
213
|
animationFrameRef.current = null;
|
|
205
214
|
}
|
|
206
215
|
|
|
207
|
-
// 停止分析器
|
|
208
|
-
analyzerRef.current?.stop();
|
|
209
|
-
|
|
210
216
|
setState((prev) => ({ ...prev, isMonitoring: false }));
|
|
211
217
|
}, []);
|
|
212
218
|
|
|
@@ -214,8 +220,8 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
214
220
|
* 重置统计数据
|
|
215
221
|
*/
|
|
216
222
|
const reset = useCallback(() => {
|
|
217
|
-
PerformanceAnalyzer.resetInstance();
|
|
218
223
|
fpsHistoryRef.current = [];
|
|
224
|
+
reportHistoryRef.current = [];
|
|
219
225
|
setState((prev) => ({
|
|
220
226
|
...prev,
|
|
221
227
|
fps: 60,
|
|
@@ -230,17 +236,8 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
230
236
|
/**
|
|
231
237
|
* 获取性能报告
|
|
232
238
|
*/
|
|
233
|
-
const getReport = useCallback(():
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
try {
|
|
237
|
-
const report =
|
|
238
|
-
analyzerRef.current.getMetricsByType?.('renderTime' as PerformanceMetricType) ?? [];
|
|
239
|
-
if (!report || report.length === 0) return [];
|
|
240
|
-
return report;
|
|
241
|
-
} catch {
|
|
242
|
-
return [];
|
|
243
|
-
}
|
|
239
|
+
const getReport = useCallback((): PerformanceReportEntry[] => {
|
|
240
|
+
return reportHistoryRef.current.slice(-100); // 返回最近 100 条记录
|
|
244
241
|
}, []);
|
|
245
242
|
|
|
246
243
|
// 自动启动
|
|
@@ -264,10 +261,10 @@ export function usePerformance(options: UsePerformanceOptions = {}): UsePerforma
|
|
|
264
261
|
}
|
|
265
262
|
|
|
266
263
|
/**
|
|
267
|
-
* 轻量级 FPS
|
|
264
|
+
* 轻量级 FPS 计数器 Hook
|
|
268
265
|
* 用于实时显示图表 FPS
|
|
269
266
|
*/
|
|
270
|
-
export function
|
|
267
|
+
export function useFpsCounter(): number {
|
|
271
268
|
const [fps, setFps] = useState(60);
|
|
272
269
|
const frameCountRef = useRef(0);
|
|
273
270
|
const lastTimeRef = useRef(performance.now());
|
|
@@ -297,3 +294,6 @@ export function useFpsMonitor(): number {
|
|
|
297
294
|
|
|
298
295
|
return fps;
|
|
299
296
|
}
|
|
297
|
+
|
|
298
|
+
/** @deprecated Use `useFpsMonitor` instead */
|
|
299
|
+
export const usePerformance = useFpsMonitor;
|
|
@@ -11,16 +11,16 @@ import { useCallback, useRef, useEffect, useState } from 'react';
|
|
|
11
11
|
* @param delay 延迟时间 (ms)
|
|
12
12
|
* @returns 防抖后的回调(包含 cancel 方法)
|
|
13
13
|
*/
|
|
14
|
-
export function useDebounce<T extends (...
|
|
14
|
+
export function useDebounce<T extends (..._args: unknown[]) => unknown>(
|
|
15
15
|
callback: T,
|
|
16
16
|
delay: number
|
|
17
17
|
): {
|
|
18
|
-
(...
|
|
18
|
+
(..._args: Parameters<T>): void;
|
|
19
19
|
cancel: () => void;
|
|
20
20
|
flush: () => void;
|
|
21
21
|
} {
|
|
22
22
|
const callbackRef = useRef(callback);
|
|
23
|
-
const
|
|
23
|
+
const _timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
24
24
|
|
|
25
25
|
// 更新 callback 引用
|
|
26
26
|
useEffect(() => {
|
|
@@ -30,22 +30,22 @@ export function useDebounce<T extends (...args: unknown[]) => unknown>(
|
|
|
30
30
|
// 清理定时器
|
|
31
31
|
useEffect(() => {
|
|
32
32
|
return () => {
|
|
33
|
-
if (
|
|
34
|
-
clearTimeout(
|
|
35
|
-
|
|
33
|
+
if (_timeoutRef.current) {
|
|
34
|
+
clearTimeout(_timeoutRef.current);
|
|
35
|
+
_timeoutRef.current = null;
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
}, []);
|
|
39
39
|
|
|
40
40
|
const debouncedCallback = useCallback(
|
|
41
|
-
(...
|
|
42
|
-
if (
|
|
43
|
-
clearTimeout(
|
|
41
|
+
(..._args: Parameters<T>) => {
|
|
42
|
+
if (_timeoutRef.current) {
|
|
43
|
+
clearTimeout(_timeoutRef.current);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
callbackRef.current(...
|
|
48
|
-
|
|
46
|
+
_timeoutRef.current = setTimeout(() => {
|
|
47
|
+
callbackRef.current(..._args);
|
|
48
|
+
_timeoutRef.current = null;
|
|
49
49
|
}, delay);
|
|
50
50
|
},
|
|
51
51
|
[delay]
|
|
@@ -53,19 +53,19 @@ export function useDebounce<T extends (...args: unknown[]) => unknown>(
|
|
|
53
53
|
|
|
54
54
|
// 添加 cancel 方法
|
|
55
55
|
const cancel = useCallback(() => {
|
|
56
|
-
if (
|
|
57
|
-
clearTimeout(
|
|
58
|
-
|
|
56
|
+
if (_timeoutRef.current) {
|
|
57
|
+
clearTimeout(_timeoutRef.current);
|
|
58
|
+
_timeoutRef.current = null;
|
|
59
59
|
}
|
|
60
60
|
}, []);
|
|
61
61
|
|
|
62
62
|
// 添加 flush 方法
|
|
63
|
-
const flush = useCallback((...
|
|
64
|
-
if (
|
|
65
|
-
clearTimeout(
|
|
66
|
-
|
|
63
|
+
const flush = useCallback((..._args: Parameters<T>) => {
|
|
64
|
+
if (_timeoutRef.current) {
|
|
65
|
+
clearTimeout(_timeoutRef.current);
|
|
66
|
+
_timeoutRef.current = null;
|
|
67
67
|
}
|
|
68
|
-
callbackRef.current(...
|
|
68
|
+
callbackRef.current(..._args);
|
|
69
69
|
}, []);
|
|
70
70
|
|
|
71
71
|
// 将 cancel 和 flush 附加到回调函数上
|
|
@@ -81,17 +81,17 @@ export function useDebounce<T extends (...args: unknown[]) => unknown>(
|
|
|
81
81
|
* @param options 节流选项
|
|
82
82
|
* @returns 节流后的回调
|
|
83
83
|
*/
|
|
84
|
-
export function useThrottle<T extends (...
|
|
84
|
+
export function useThrottle<T extends (..._args: unknown[]) => unknown>(
|
|
85
85
|
callback: T,
|
|
86
86
|
limit: number,
|
|
87
87
|
options?: {
|
|
88
88
|
leading?: boolean;
|
|
89
89
|
trailing?: boolean;
|
|
90
90
|
}
|
|
91
|
-
): (...
|
|
91
|
+
): (..._args: Parameters<T>) => void {
|
|
92
92
|
const callbackRef = useRef(callback);
|
|
93
93
|
const lastCallTimeRef = useRef<number>(0);
|
|
94
|
-
const
|
|
94
|
+
const _timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
95
95
|
const lastArgsRef = useRef<Parameters<T> | null>(null);
|
|
96
96
|
const lastThisRef = useRef<unknown>(null);
|
|
97
97
|
|
|
@@ -105,36 +105,36 @@ export function useThrottle<T extends (...args: unknown[]) => unknown>(
|
|
|
105
105
|
// 清理
|
|
106
106
|
useEffect(() => {
|
|
107
107
|
return () => {
|
|
108
|
-
if (
|
|
109
|
-
clearTimeout(
|
|
110
|
-
|
|
108
|
+
if (_timeoutRef.current) {
|
|
109
|
+
clearTimeout(_timeoutRef.current);
|
|
110
|
+
_timeoutRef.current = null;
|
|
111
111
|
}
|
|
112
112
|
};
|
|
113
113
|
}, []);
|
|
114
114
|
|
|
115
115
|
const throttledCallback = useCallback(
|
|
116
|
-
(...
|
|
116
|
+
(..._args: Parameters<T>) => {
|
|
117
117
|
const now = Date.now();
|
|
118
118
|
const elapsed = now - lastCallTimeRef.current;
|
|
119
119
|
|
|
120
120
|
// 保存调用上下文和参数
|
|
121
|
-
lastArgsRef.current =
|
|
122
|
-
lastThisRef.current =
|
|
121
|
+
lastArgsRef.current = _args;
|
|
122
|
+
lastThisRef.current = _args.length > 0 ? _args[0] : null;
|
|
123
123
|
|
|
124
124
|
if (elapsed >= limit) {
|
|
125
125
|
// 超过限制,立即执行
|
|
126
|
-
if (
|
|
127
|
-
clearTimeout(
|
|
128
|
-
|
|
126
|
+
if (_timeoutRef.current) {
|
|
127
|
+
clearTimeout(_timeoutRef.current);
|
|
128
|
+
_timeoutRef.current = null;
|
|
129
129
|
}
|
|
130
130
|
lastCallTimeRef.current = now;
|
|
131
|
-
callbackRef.current(...
|
|
131
|
+
callbackRef.current(..._args);
|
|
132
132
|
lastArgsRef.current = null;
|
|
133
133
|
lastThisRef.current = null;
|
|
134
|
-
} else if (trailing && !
|
|
134
|
+
} else if (trailing && !_timeoutRef.current) {
|
|
135
135
|
// 在节流窗口结束时执行最后一次调用
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
_timeoutRef.current = setTimeout(() => {
|
|
137
|
+
_timeoutRef.current = null;
|
|
138
138
|
lastCallTimeRef.current = Date.now();
|
|
139
139
|
if (lastArgsRef.current && lastThisRef.current !== null) {
|
|
140
140
|
callbackRef.current(...lastArgsRef.current);
|
|
@@ -154,7 +154,7 @@ export function useThrottle<T extends (...args: unknown[]) => unknown>(
|
|
|
154
154
|
* 请求动画帧 Hook
|
|
155
155
|
* 用于优化动画性能
|
|
156
156
|
*/
|
|
157
|
-
export function useAnimationFrame(callback: (
|
|
157
|
+
export function useAnimationFrame(callback: (_time: number) => void, enabled = true): void {
|
|
158
158
|
const callbackRef = useRef(callback);
|
|
159
159
|
const animationFrameRef = useRef<number | null>(null);
|
|
160
160
|
|
|
@@ -172,8 +172,8 @@ export function useAnimationFrame(callback: (time: number) => void, enabled = tr
|
|
|
172
172
|
return;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
const loop = (
|
|
176
|
-
callbackRef.current(
|
|
175
|
+
const loop = (_time: number) => {
|
|
176
|
+
callbackRef.current(_time);
|
|
177
177
|
animationFrameRef.current = requestAnimationFrame(loop);
|
|
178
178
|
};
|
|
179
179
|
|