fix: barchart, piechart flash issue

This commit is contained in:
jialin
2026-06-14 19:14:18 +08:00
committed by jialin
parent 736a853192
commit 3e2133753d
2 changed files with 62 additions and 6 deletions
+33 -3
View File
@@ -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<BarChartProps> = (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<ResizeObserver | null>(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<BarChartProps> = (props) => {
}
return (
<div style={{ width, height, position: 'relative' }}>
<div
ref={measureRef}
style={{ width: width || '100%', height, position: 'relative' }}
>
<Chart
ref={chartRef as any}
options={options as any}
height={height}
width={width || '100%'}
width={measuredWidth || width || '100%'}
/>
{loading && (
<div
+29 -3
View File
@@ -2,7 +2,13 @@ import useCoolColors from '@/hooks/use-cool-colors';
import { Chart } from '@gpustack/core-ui';
import { formatLargeNumber } from '@gpustack/core-ui/utils';
import { Empty, Spin, theme } from 'antd';
import React, { useMemo, useRef } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState
} from 'react';
export interface PieChartItem {
name: string;
@@ -37,6 +43,26 @@ const PieChart: React.FC<PieChartProps> = ({
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<ResizeObserver | null>(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<PieChartProps> = ({
const displayTotal = total ?? data.reduce((sum, item) => sum + item.value, 0);
return (
<div style={{ width, height, position: 'relative' }}>
<div ref={measureRef} style={{ width, height, position: 'relative' }}>
<Chart
ref={chartRef as any}
options={options as any}
height={height}
width={width}
width={measuredWidth || width}
/>
<div
style={{