@mmorrii

Как убрать мерцание графика при перерисовке компонента?

У меня есть компонент с таблицей TablePanel, данные для которой я перебираю с помощью map. В одной из ячейки таблицы есть компонент с графиком LineChartWithoutLegend. Для графика использую библиотеку nivo.

Данные в wsResponseData обновляются ежесекундно и получается так, что при переборе этих данных, ячейки таблицы перерисовываются и вместе с ними компонент с графиком, из-за чего происходит эффект мерцания.

Пробовала выносить график вне map, такого мерцания нет. Также на странице есть другой линейный график у которого мерцания нет.

Подскажите что можно сделать, чтобы избежать такого поведения.

66732dd0d286a408321710.gif

export const TablePanel = () => {
    const wsResponseData = useContext(WsDataContext)
    const httpResponseData = useContext(HttpDataContext)

    return (
        <Table>
            <thead>
                //...
            </thead>
            <tbody>
                { httpResponseData.map(httpData => (
                    wsResponseData[httpData.Names[0]].slice(-1).map((data) => (
                        <tr key={data.keyId}>
                            //...
                            <td>{data.cpu_stats.online_cpus ?
                                <div style={{height: "46px", width: "140px"}}>
                                    <LineChartWithoutLegend containerName={data.name}/>
                                </div> :
                                "null"}
                            </td>
                        </tr>
                    ))
                ))}
            </tbody>
        </Table>
    )
}


export const LineChartWithoutLegend = ({ containerName }: { containerName: string }) => {
    const wsResponseData = useContext(WsDataContext)

    const chartDataRef = useRef<CoordsType[]>(initialLineChartData)

    useEffect(() => {
        const currentTime = getCurrentTime()
        const lastElemTime = chartDataRef.current[chartDataRef.current.length - 1]?.x

        if (currentTime === lastElemTime) return
        
        wsResponseData[containerName]?.slice(-1).map(wsData => {
            chartDataRef.current.push({x: currentTime, y: cpuUsage(wsData)})
        })

        if (chartDataRef.current.length > 30) {
            chartDataRef.current = chartDataRef.current?.slice(-30);
        }
    }, [containerName, wsResponseData]);

    return (
        <ResponsiveLineCanvas
            data={[
                {
                    id: "CPU usage monitoring",
                    data: chartDataRef.current,
                },
            ]}
            // другие настройки для графика
        />
    )
}
  • Вопрос задан
  • 188 просмотров
Пригласить эксперта
Ответы на вопрос 1
@odissey_nemo
Программист, ГИС-системы, растры, космоснимки
Логично сделать так, чтобы компоненты на экране не перерисовывались отдельно друг от друга на каждый чих , а перерисовывались после обновления всех данных в едином цикле. Со средой разработки и библиотеками графики указанного проекта не знаком, но принципы останутся общими, полагаю.
Следует либо останавливать вывод на время обновления, либо вывод организовать в какой то дополнительный буфер (в памяти?), с которого уже и обновлять на регулярной или событийной основе.
Например, при написании графических программ на Java через Swing, также и на Delphi и C# с их библиотеками, полезным оказалось иметь главный буфер в виде Bitmap размером с окно программы, который и использовался для обновления по произвольным событиям (скажем, периодическое обновление всего окна).
Вызвал все процедуры обновления и только тогда сказал repaint. Всё обновится с результирущего буфера, без всякого мигания.
Т.е. смысл в том, чтобы repaint вызывался не из каждого компонента отдельно и асинхронно, а один раз в подходящий момент, после гарантированного завершения всех срочных модификаций или даже на регулярной основе. Мерцания быть не должно.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы