React

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > React > React ECharts动态折线图

React + ECharts动态折线图完整实现代码

作者:程序媛ing

ECharts作为国内最流行的可视化库,其实内置了非常强大的线条特效和动画能力,这篇文章主要介绍了React + ECharts动态折线图的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

上周项目接到一个新的需求,实现一个每3秒动态更新数据 的折线图,刚开始有点懵逼了,echarts如何能做成动态的,最终实现的效果如下:

实现思路:

  1. 初始化阶段:创建 ECharts 实例
  2. 第一次数据加载(loadData):填满 180 点窗口
  3. 定时更新(smoothAdvance):每 3 秒滑动窗口
  4. 图表渲染(updateChart)
  5. 图表自适应(ResizeObserver)

遇到的问题:

1. 第一次初始化数据的最后一个点和后续第一个点之间断裂(不连线)

2. 第一次初始化数据的最后的连接点跟3s后更新数据的第一个点的连接线Tooltip显示undifiend

代码实现如下:

初始化阶段:创建 ECharts 实例

const initChart = () => {  
    if (!chartRef.current) return;  
    chartInstance.current = echarts.init(chartRef.current);  

    chartInstance.current.setOption({  
        title: {text: labelList.realTime_rx_or_tx_rate, left: "center"},  
        tooltip: {  
            trigger: "axis",  
            formatter: params => {  
               //避免Tooltip显示undifiend
                const validParams = params.filter(p => p.data != null && !Number.isNaN(p.data));  
                if (!validParams.length) return "";  
                let text = `${params[0].axisValue}
`;  
                validParams.forEach(item => {  
                    text += `${item.marker}${item.seriesName}: ${formatSpeedForStats(item.data, "bit")}
`;  
                });  
                return text;  
            }  
        },  
        grid: {left: 120, right: 20, bottom: 40, top: 80},  
        xAxis: {type: "category", boundaryGap: false, data: []},  
        yAxis: {  
            type: "value",  
            axisLabel: {  
                formatter: value => formatSpeedForStats(value, "bit")  
            },  
            splitLine: {lineStyle: {type: "dashed"}}  
        },  
        series: [  
            {  
                name: labelList.tx,  
                type: "line",  
                smooth: false,  
                showSymbol: false,  
                connectNulls: true,  
                areaStyle: {color: "rgba(0, 180, 0, 0.2)"},  
                lineStyle: {color: "#00c853", width: 2},  
                data: []  
            },  
            {  
                name: labelList.rx,  
                type: "line",  
                smooth: false,  
                showSymbol: false,  
                connectNulls: true,  
                areaStyle: {color: "rgba(0, 102, 255, 0.2)"},  
                lineStyle: {color: "#0066ff", width: 2},  
                data: []  
            }  
        ]  
    });  
};
echarts数据处理方法

const processDataPoints = result => {  
    if (!*Array*.isArray(result) || result.length < 2) return {newTimeData: [], newTxData: [], newRxData: []};  

    const newTimeData = [];  
    const newTxData = [];  
    const newRxData = [];  

    for (let i = 1; i < result.length; i++) {  
        const [t2, rx2, , tx2] = result[i];  
        const [t1, rx1, , tx1] = result[i - 1];  

        const txRate = ((tx2 - tx1) * 8) / 1024 / (t2 - t1);  
        const rxRate = ((rx2 - rx1) * 8) / 1024 / (t2 - t1);  
        const timeStr = new *Date*(t2 * 1000).toLocaleTimeString("zh-CN", {hour12: false});  

        newTimeData.push(timeStr);  
        newTxData.push(parseFloat(txRate.toFixed(2)));  
        newRxData.push(parseFloat(rxRate.toFixed(2)));  
    }  

    return {newTimeData, newTxData, newRxData};  
};

第一次数据加载:填满180点窗口

const loadData = async () => {  
    try {  
        const res = await *api*.request({  
            luci: {  
                getRealtimeStats: {mode: "interface", device}  
            }  
        });  

        const result = res?.result;  


        const {newTimeData, newTxData, newRxData} = processDataPoints(result);  

        // 初始化用 NaN 填充,避免折线断裂  
        const fullTime = *Array*(TOTAL_POINTS).fill("");  
        const fullTx = *Array*(TOTAL_POINTS).fill(*NaN*);  
        const fullRx = *Array*(TOTAL_POINTS).fill(*NaN*);  

        const len = newTimeData.length;  
        const start = TOTAL_POINTS - len;  

        for (let i = 0; i < len; i++) {  
            fullTime[start + i] = newTimeData[i];  
            fullTx[start + i] = newTxData[i];  
            fullRx[start + i] = newRxData[i];  
        }  
        setTimeData(fullTime);  
        setTxData(fullTx);  
        setRxData(fullRx);  
    } catch (err) {  
        *console*.error(err);  
    }  
};

定时更新:每3s滑动窗口 删除旧的三个点,添加最新点

const smoothAdvance = async () => {  
    try {  
        const res = await *api*.request({  
            luci: {  
                getRealtimeStats: {mode: "interface", device}  
            }  
        });  

        const result = res?.result;  
               const {newTimeData, newTxData, newRxData} = processDataPoints(result.slice(-4));  
        setTimeData(prev => {  
            const kept = prev.slice(3);  
            const lastTime = kept[kept.length - 1];
            
            //避免重复点

            const filtered = newTimeData.filter(t => t !== lastTime);  

            return [...kept, ...filtered];  
        });  
      

        //null值用 NaN 填充,避免折线断裂 同时避免在连线触发tooltip显示(应该在每个点显示)
      
        setTxData(prev => {  
            const kept = prev.slice(3).map(v => (v == null ? "NaN" : v));  
            return [...kept, ...newTxData];  
        });  

        setRxData(prev => {  
            const kept = prev.slice(3).map(v => (v == null ? "NaN": v));  
            return [...kept, ...newRxData];  
        });  
    } catch (e) {  
        *console*.error("smoothAdvance error:", e);  
    }  
};

图表渲染 只更新坐标轴 数据数组

const updateChart = (timeArr, txArr, rxArr) => {  
    if (!chartInstance.current) return;  
    chartInstance.current.setOption(  
        {  
            xAxis: {data: timeArr},  
            series: [{data: txArr}, {data: rxArr}]  
        },  
        false  
    );  
};

useEffect(() => {  
    if (chartInstance.current) {  
        updateChart(timeData, txData, rxData);  
    }  
}, [timeData, txData, rxData]);

图表自适应 (ResizeObserver)

useEffect(() => {  
    const observer = new ResizeObserver(() => {  
        chartInstance.current?.resize();  
    });  
    if (chartRef.current) observer.observe(chartRef.current);  
    return () => observer.disconnect();  
}, []);

图表初始化渲染(第一次加载数据渲染)

useEffect(() => {  
  if (!chartInstance.current) initChart();  
    loadData();  
    const timer = setInterval(smoothAdvance, 3000);  
    return () => clearInterval(timer);  
}, []);

总结 

到此这篇关于React + ECharts动态折线图完整实现代码的文章就介绍到这了,更多相关React ECharts动态折线图内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文