From f800e2e3b6ad5497a26b4ac7158e78e638ace06e Mon Sep 17 00:00:00 2001 From: wonfen Date: Thu, 27 Mar 2025 13:11:39 +0800 Subject: [PATCH] refactor: simplify graph code and adjust text margins --- UPDATELOG.md | 1 + .../home/enhanced-traffic-graph.tsx | 237 ++++-------------- 2 files changed, 52 insertions(+), 186 deletions(-) diff --git a/UPDATELOG.md b/UPDATELOG.md index a6f5a68d..aa7935a2 100644 --- a/UPDATELOG.md +++ b/UPDATELOG.md @@ -17,6 +17,7 @@ #### 优化了: - 重构了后端内核管理逻辑,更轻量化和有效的管理内核,提高了性能和稳定性 - 前端统一刷新应用数据,优化数据获取和刷新逻辑 + - 优化首页流量图表代码,调整图表文字边距 ## v2.2.2 diff --git a/src/components/home/enhanced-traffic-graph.tsx b/src/components/home/enhanced-traffic-graph.tsx index 9c8d2ff1..b9aee07e 100644 --- a/src/components/home/enhanced-traffic-graph.tsx +++ b/src/components/home/enhanced-traffic-graph.tsx @@ -39,69 +39,35 @@ export interface EnhancedTrafficGraphRef { // 时间范围类型 type TimeRange = 1 | 5 | 10; // 分钟 -// 创建一个明确的类型 +// 数据点类型 type DataPoint = ITrafficItem & { name: string; timestamp: number }; -// 控制帧率的工具函数 -const FPS_LIMIT = 1; // 限制为1fps,因为数据每秒才更新一次 -const FRAME_MIN_TIME = 1000 / FPS_LIMIT; // 每帧最小时间间隔,即1000ms - -// 全局存储流量数据历史记录 -declare global { - interface Window { - trafficHistoryData?: DataPoint[]; - trafficHistoryStyle?: "line" | "area"; - trafficHistoryTimeRange?: TimeRange; - } -} - -// 初始化全局存储 -if (typeof window !== "undefined" && !window.trafficHistoryData) { - window.trafficHistoryData = []; - window.trafficHistoryStyle = "area"; - window.trafficHistoryTimeRange = 10; -} - /** * 增强型流量图表组件 - * 基于 Recharts 实现,支持线图和面积图两种模式 */ export const EnhancedTrafficGraph = memo(forwardRef( (props, ref) => { const theme = useTheme(); const { t } = useTranslation(); - // 从全局变量恢复状态 - const [timeRange, setTimeRange] = useState( - window.trafficHistoryTimeRange || 10 - ); - const [chartStyle, setChartStyle] = useState<"line" | "area">( - window.trafficHistoryStyle || "area" - ); - - // 使用useRef存储数据,避免不必要的重渲染 - const dataBufferRef = useRef([]); - // 只为渲染目的的状态 + // 基础状态 + const [timeRange, setTimeRange] = useState(10); + const [chartStyle, setChartStyle] = useState<"line" | "area">("area"); const [displayData, setDisplayData] = useState([]); - // 帧率控制 - const lastUpdateTimeRef = useRef(0); - const pendingUpdateRef = useRef(false); - const rafIdRef = useRef(null); + // 数据缓冲区 + const dataBufferRef = useRef([]); // 根据时间范围计算保留的数据点数量 const getMaxPointsByTimeRange = useCallback( - (minutes: TimeRange): number => { - // 使用更低的采样率来减少点的数量,每2秒一个点而不是每秒一个点 - return minutes * 30; // 每分钟30个点(每2秒1个点) - }, - [], + (minutes: TimeRange): number => minutes * 30, + [] ); - // 最大数据点数量 - 基于选择的时间范围 + // 最大数据点数量 const MAX_BUFFER_SIZE = useMemo( () => getMaxPointsByTimeRange(10), - [getMaxPointsByTimeRange], + [getMaxPointsByTimeRange] ); // 颜色配置 @@ -113,150 +79,51 @@ export const EnhancedTrafficGraph = memo(forwardRef( tooltip: theme.palette.background.paper, text: theme.palette.text.primary, }), - [theme], + [theme] ); // 切换时间范围 const handleTimeRangeClick = useCallback(() => { setTimeRange((prevRange) => { // 在1、5、10分钟之间循环切换 - const newRange = prevRange === 1 ? 5 : prevRange === 5 ? 10 : 1; - window.trafficHistoryTimeRange = newRange; // 保存到全局 - return newRange; + return prevRange === 1 ? 5 : prevRange === 5 ? 10 : 1; }); }, []); // 初始化数据缓冲区 useEffect(() => { - let initialBuffer: DataPoint[] = []; - - // 如果全局有保存的数据,优先使用 - if (window.trafficHistoryData && window.trafficHistoryData.length > 0) { - initialBuffer = [...window.trafficHistoryData]; - - // 确保数据长度符合要求 - if (initialBuffer.length > MAX_BUFFER_SIZE) { - initialBuffer = initialBuffer.slice(-MAX_BUFFER_SIZE); - } else if (initialBuffer.length < MAX_BUFFER_SIZE) { - // 如果历史数据不足,则在前面补充空数据 - const now = Date.now(); - const oldestTimestamp = initialBuffer.length > 0 - ? initialBuffer[0].timestamp - : now - 10 * 60 * 1000; - - const additionalPoints = MAX_BUFFER_SIZE - initialBuffer.length; - const timeInterval = initialBuffer.length > 0 - ? (initialBuffer[0].timestamp - (now - 10 * 60 * 1000)) / additionalPoints - : (10 * 60 * 1000) / MAX_BUFFER_SIZE; - - const emptyPrefix: DataPoint[] = Array.from( - { length: additionalPoints }, - (_, index) => { - const pointTime = oldestTimestamp - (additionalPoints - index) * timeInterval; - const date = new Date(pointTime); - - return { - up: 0, - down: 0, - timestamp: pointTime, - name: date.toLocaleTimeString("en-US", { - hour12: false, - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - }), - }; - } - ); - - initialBuffer = [...emptyPrefix, ...initialBuffer]; + // 创建初始空数据 + const now = Date.now(); + const tenMinutesAgo = now - 10 * 60 * 1000; + + const initialBuffer = Array.from( + { length: MAX_BUFFER_SIZE }, + (_, index) => { + const pointTime = + tenMinutesAgo + index * ((10 * 60 * 1000) / MAX_BUFFER_SIZE); + const date = new Date(pointTime); + + return { + up: 0, + down: 0, + timestamp: pointTime, + name: date.toLocaleTimeString("en-US", { + hour12: false, + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + }), + }; } - } else { - // 没有历史数据时,创建空的初始缓冲区 - const now = Date.now(); - const tenMinutesAgo = now - 10 * 60 * 1000; - - initialBuffer = Array.from( - { length: MAX_BUFFER_SIZE }, - (_, index) => { - const pointTime = - tenMinutesAgo + index * ((10 * 60 * 1000) / MAX_BUFFER_SIZE); - const date = new Date(pointTime); - - return { - up: 0, - down: 0, - timestamp: pointTime, - name: date.toLocaleTimeString("en-US", { - hour12: false, - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - }), - }; - } - ); - } + ); dataBufferRef.current = initialBuffer; - window.trafficHistoryData = initialBuffer; // 保存到全局 // 更新显示数据 const pointsToShow = getMaxPointsByTimeRange(timeRange); setDisplayData(initialBuffer.slice(-pointsToShow)); - - // 清理函数,取消任何未完成的动画帧 - return () => { - if (rafIdRef.current !== null) { - cancelAnimationFrame(rafIdRef.current); - rafIdRef.current = null; - } - }; }, [MAX_BUFFER_SIZE, getMaxPointsByTimeRange, timeRange]); - // 处理数据更新并控制帧率的函数 - const updateDisplayData = useCallback(() => { - if (pendingUpdateRef.current) { - pendingUpdateRef.current = false; - - // 根据当前时间范围计算需要显示的点数 - const pointsToShow = getMaxPointsByTimeRange(timeRange); - // 从缓冲区中获取最新的数据点 - const newDisplayData = dataBufferRef.current.slice(-pointsToShow); - setDisplayData(newDisplayData); - } - - rafIdRef.current = null; - }, [timeRange, getMaxPointsByTimeRange]); - - // 节流更新函数 - const throttledUpdateData = useCallback(() => { - pendingUpdateRef.current = true; - - const now = performance.now(); - const timeSinceLastUpdate = now - lastUpdateTimeRef.current; - - if (rafIdRef.current === null) { - if (timeSinceLastUpdate >= FRAME_MIN_TIME) { - // 如果距离上次更新已经超过最小帧时间,立即更新 - lastUpdateTimeRef.current = now; - rafIdRef.current = requestAnimationFrame(updateDisplayData); - } else { - // 否则,在适当的时间进行更新 - const timeToWait = FRAME_MIN_TIME - timeSinceLastUpdate; - setTimeout(() => { - lastUpdateTimeRef.current = performance.now(); - rafIdRef.current = requestAnimationFrame(updateDisplayData); - }, timeToWait); - } - } - }, [updateDisplayData]); - - // 监听时间范围变化,更新显示数据 - useEffect(() => { - throttledUpdateData(); - }, [timeRange, throttledUpdateData]); - // 添加数据点方法 const appendData = useCallback((data: ITrafficItem) => { // 安全处理数据 @@ -281,24 +148,24 @@ export const EnhancedTrafficGraph = memo(forwardRef( timestamp: timestamp, }; - // 更新ref,但保持原数组大小 + // 更新缓冲区,保持原数组大小 const newBuffer = [...dataBufferRef.current.slice(1), newPoint]; dataBufferRef.current = newBuffer; - // 保存到全局变量 - window.trafficHistoryData = newBuffer; - - // 使用节流更新显示数据 - throttledUpdateData(); - }, [throttledUpdateData]); + // 更新显示数据 + const pointsToShow = getMaxPointsByTimeRange(timeRange); + setDisplayData(newBuffer.slice(-pointsToShow)); + }, [timeRange, getMaxPointsByTimeRange]); + + // 监听时间范围变化,更新显示数据 + useEffect(() => { + const pointsToShow = getMaxPointsByTimeRange(timeRange); + setDisplayData(dataBufferRef.current.slice(-pointsToShow)); + }, [timeRange, getMaxPointsByTimeRange]); // 切换图表样式 const toggleStyle = useCallback(() => { - setChartStyle((prev) => { - const newStyle = prev === "line" ? "area" : "line"; - window.trafficHistoryStyle = newStyle; // 保存到全局 - return newStyle; - }); + setChartStyle((prev) => prev === "line" ? "area" : "line"); }, []); // 暴露方法给父组件 @@ -308,13 +175,12 @@ export const EnhancedTrafficGraph = memo(forwardRef( appendData, toggleStyle, }), - [appendData, toggleStyle], + [appendData, toggleStyle] ); // 格式化工具提示内容 const formatTooltip = useCallback((value: number, name: string, props: any) => { const [num, unit] = parseTraffic(value); - // 使用props.dataKey判断是上传还是下载 return [`${num} ${unit}/s`, props?.dataKey === "up" ? t("Upload") : t("Download")]; }, [t]); @@ -327,7 +193,6 @@ export const EnhancedTrafficGraph = memo(forwardRef( // 格式化X轴标签 const formatXLabel = useCallback((value: string) => { if (!value) return ""; - // 只显示小时和分钟 const parts = value.split(":"); return `${parts[0]}:${parts[1]}`; }, []); @@ -352,7 +217,7 @@ export const EnhancedTrafficGraph = memo(forwardRef( isAnimationActive: false, // 禁用动画以减少CPU使用 }), []); - // 曲线类型 - 使用线性曲线避免错位 + // 曲线类型 const curveType = "monotone"; return ( @@ -386,7 +251,7 @@ export const EnhancedTrafficGraph = memo(forwardRef( tick={{ fontSize: 10, fill: colors.text }} tickLine={{ stroke: colors.grid }} axisLine={{ stroke: colors.grid }} - width={40} + width={43} domain={[0, "auto"]} /> ( tick={{ fontSize: 10, fill: colors.text }} tickLine={{ stroke: colors.grid }} axisLine={{ stroke: colors.grid }} - width={40} + width={43} domain={[0, "auto"]} padding={{ top: 10, bottom: 0 }} />