From 3e2133753dd305f8773177a8b17ea0c9efc5f3dc Mon Sep 17 00:00:00 2001 From: jialin Date: Sun, 14 Jun 2026 19:00:57 +0800 Subject: [PATCH] fix: barchart, piechart flash issue --- src/pages/_components/bar-chart/index.tsx | 36 +++++++++++++++++++++-- src/pages/_components/pie-chart/index.tsx | 32 ++++++++++++++++++-- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/pages/_components/bar-chart/index.tsx b/src/pages/_components/bar-chart/index.tsx index a8643124..94f88397 100644 --- a/src/pages/_components/bar-chart/index.tsx +++ b/src/pages/_components/bar-chart/index.tsx @@ -3,7 +3,13 @@ import { Chart } from '@gpustack/core-ui'; import { formatLargeNumber } from '@gpustack/core-ui/utils'; import { Empty, Spin, theme } from 'antd'; import _ from 'lodash'; -import React, { useEffect, useMemo, useRef } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState +} from 'react'; export interface BarSeriesItem { name: string; @@ -50,6 +56,27 @@ const BarChart: React.FC = (props) => { const chartRef = useRef<{ chart: any } | null>(null); const generateCoolColors = useCoolColors(); + // ECharts reads the DOM width at init time; with a "100%" width it can pick + // up a stale/tiny value while the flex child's layout is still resolving, + // rendering every bar squeezed at the left edge until its internal (throttled) + // ResizeObserver corrects it ~100ms later — a visible blue-sliver flash. We + // measure the container ourselves via a callback ref (which fires during + // commit, before paint) and feed ECharts an explicit pixel width, so the very + // first render is already correct. No gating, so the chart mounts with no + // extra delay; the ResizeObserver keeps the width in sync afterwards. + const resizeObserverRef = useRef(null); + const [measuredWidth, setMeasuredWidth] = useState(0); + const measureRef = useCallback((node: HTMLDivElement | null) => { + resizeObserverRef.current?.disconnect(); + if (!node) return; + setMeasuredWidth(node.clientWidth); + resizeObserverRef.current = new ResizeObserver(() => { + setMeasuredWidth(node.clientWidth); + }); + resizeObserverRef.current.observe(node); + }, []); + useEffect(() => () => resizeObserverRef.current?.disconnect(), []); + const dynamicColors = useMemo( () => generateCoolColors(seriesData.length), [seriesData.length, generateCoolColors] @@ -289,12 +316,15 @@ const BarChart: React.FC = (props) => { } return ( -
+
{loading && (
= ({ const chartRef = useRef<{ chart: any } | null>(null); const generateCoolColors = useCoolColors(); + // ECharts reads the DOM width at init time; with a "100%" width it can pick + // up a stale/tiny value while layout is still resolving, briefly rendering + // the donut undersized before its internal (throttled) ResizeObserver + // corrects it — a visible flash. We measure the container via a callback ref + // (fires during commit, before paint) and feed ECharts an explicit pixel + // width so the first render is already correct. The ResizeObserver keeps it + // in sync afterwards. + const resizeObserverRef = useRef(null); + const [measuredWidth, setMeasuredWidth] = useState(0); + const measureRef = useCallback((node: HTMLDivElement | null) => { + resizeObserverRef.current?.disconnect(); + if (!node) return; + setMeasuredWidth(node.clientWidth); + resizeObserverRef.current = new ResizeObserver(() => { + setMeasuredWidth(node.clientWidth); + }); + resizeObserverRef.current.observe(node); + }, []); + useEffect(() => () => resizeObserverRef.current?.disconnect(), []); + const colors = useMemo(() => { const generatedColors = generateCoolColors(data.length + colorOffset); return generatedColors.slice(colorOffset); @@ -142,12 +168,12 @@ const PieChart: React.FC = ({ const displayTotal = total ?? data.reduce((sum, item) => sum + item.value, 0); return ( -
+