美文网首页
基于React实现流量趋势图同轴联动的效果(Echarts实现)

基于React实现流量趋势图同轴联动的效果(Echarts实现)

作者: 蜀城走马 | 来源:发表于2022-03-03 19:31 被阅读0次

    前言:

    基于业务需求,需要实现多个系列的基于时间的流量趋势图折线图,实现联动的x轴的缩放和滚动浏览。之前用d3定制化了一版,但是基于后期需求扩展考虑,d3定制化开发成本较高,所以考虑基于Echarts实现。

    1、基本效果截图

    image.png

    2、基于React自定义hook的代码实现:

    SeriesChart.tsx组件封装:

    import React, { useState, useEffect, useRef } from 'react';
    import * as echarts from 'echarts';
    import ReactEcharts from 'echarts-for-react';
    import { dataTypeConfigList } from "../utils";
    
    export default (props: any) => {
    
        const { dataConfig, brushCallback, brushRange } = props;
        const [option, setOption] = useState<any>(null);
        const chartRef: any = useRef(null);
    
        // 由于基于文档编写使用,此处用mock数据,实际业务需要定制化修改数据处理相关代码
        function getRandomData() {
            let base = +new Date(1988, 9, 3);
            let oneDay = 24 * 3600 * 1000;
    
            let data = [[base, Math.random() * 300]];
    
            for (let i = 1; i < 2000; i++) {
                let now = new Date((base += oneDay));
                data.push([+now, Math.round((Math.random() - 0.5) * 20 + data[i - 1][1])]);
            }
    
            return data;
        }
    
        useEffect(() => {
        
            type EChartsOption = echarts.EChartOption;
    
            let option: EChartsOption;
    
            let series = dataConfig.series.map((item: any) => {
                let data = getRandomData();
    
                return {                                     
                    name: item.title,
                    type: 'line',
                    smooth: true,
                    symbol: 'none',
                    areaStyle: {},
                    // sampling: 'lttb', //开启大数量优化算法
                    data: data
                }
            })
    
            option = {
                color: ['#4cd698', '#4e5ad5', '#4eade9', '#f4914e', '#fcb75b', '#ffe180'],
                tooltip: {
                    trigger: 'axis',
                    position: function (pt) {
                        return [pt[0], '10%'];            
                    }
                },
                title: {
                    left: 'left',
                    text: dataConfig.title
                },
                grid: {
                    left: 50,
                    right: 40
                },
                legend: {
                    show: true,
                    icon: 'rect'
                },
                brush: {
                    xAxisIndex: 'all',
                    brushLink: 'all',
                    outOfBrush: {
                        colorAlpha: 0.1
                    }
                },
                toolbox: {
                    feature: {
                        brush: {
                            type: ['lineX', 'clear']
                        },
                        restore: {},
                        saveAsImage: {}
                    }
                },
                xAxis: {
                    type: 'time',
                    boundaryGap: false
                },
                yAxis: {
                    type: 'value',
                    boundaryGap: [0, '100%']
                },
                dataZoom: [
                    {
                        type: 'inside',
                        start: 0,
                        end: 10,
                        zoomLock: true
                    },
                    {
                        start: 0,
                        end: 10,
                        zoomLock: true
                    }
                ],
                series: series
            };
    
            setOption(option);
    
            setTimeout(() => {
                let chartEl: any = chartRef?.current?.getEchartsInstance();
                chartEl.group = 'flowChart';
                echarts.connect('flowChart');
                chartEl.on('brushEnd', (e: any) => {
                    //  笔刷框选x轴的范围数值
                    let range = e.areas[0].coordRange;
                    brushCallback(range);
                })
            })
        }, []);
    
        useEffect(() => {
            let chartEl: any = chartRef?.current?.getEchartsInstance();
    
            brushRange && chartEl && chartEl.dispatchAction({
                type: 'brush',
                areas: [
                    {
                        brushType: 'lineX',
                        coordRange: brushRange, 
                        xAxisIndex: 0
                    }
                ]
            });
        }, [brushRange])
    
        return <>
            {
                option && <ReactEcharts ref={chartRef}
                    option={option}
                    style={{ height: '240px', width: '100%' }}
                />
            }
        </>
    
    }
    

    utils数据配置文件代码如下:

    export const dataTypeConfigList = [
        {
            title: '流量',
            type: 'flow',
            series: [
                { title: '总流量', key: 'totalByte' },
                { title: '进网流量', key: 'inboundByte' },
                { title: '出网流量', key: 'outboundByte' },
            ]
        },
        {
            title: '数据包',
            type: 'package',
            series: [
                { title: '总数据包', key: 'totalPackage' },
                { title: '进网数据包', key: 'inboundPackage' },
                { title: '出网数据包', key: 'outboundPackage' },
            ]
        }
    ];
    

    实际使用的demo.tsx演示文件:

    import React, { useState } from 'react';
    import { Card } from 'antd';
    import SeriesChart from './componets/SeriesChart';
    import { dataTypeConfigList } from "./utils";
    
    export default () => {
        const [brushRange, setBrushRange] = useState<Array<any>>();
    
        const brushCallback = (range: Array<any>) => {
            setBrushRange(range);
        }
    
        return (
            <div className='dashboard-page'>
                <Card style={{ marginTop: 10 }}>
                    {
                        dataTypeConfigList.map((item: any) => {
                            return <SeriesChart dataConfig={item} key={item.type} brushCallback={brushCallback} brushRange={brushRange} />
                        })
                    }
                </Card>
            </div>
        );
    }
    
    

    3、基本说明

    基于基础代码部分,不用做过多说明了,这里只说主要的实现点和实现方案。图表联动时,需要基于Echarts图表实例的group属性,然后调用echarts.connect('flowChart')函数进行将多组图表关联,进行图表联动。这边的flowChart是我自己命名的group属性名。此处用setTimeout的原因是,需要多组echarts图表实例进行初始化之后,再进行connect才有用,所以此处这样处理。没有找到类似React中有类似Vue的$nextTick的API,如果大家有更好的处理方式,就在下方留言给予指正。
    至于刷取框选部分,由于我需要考虑系统性能,在刷取结束时进行多图的统一框选,并且brush框选是不受group联动控制的,所以这样处理。
    使用ReactEcharts插件的原因是,该插件将Echarts基于React进行了二次封装,在基于窗口的大小尺寸改变时,图表能自动进行重绘。大家想直接使用Echarts也是可以的,就是相应的界面展示优化需要定制化处理而已。

    4、写在最后

    后续我会将之前d3定制化开发的版本进行分享,d3的优点就是方便直接改变样式进行主题切换处理;当然缺点也很明显就是开发维护成本高。

    相关文章

      网友评论

          本文标题:基于React实现流量趋势图同轴联动的效果(Echarts实现)

          本文链接:https://www.haomeiwen.com/subject/joeurrtx.html