React echarts封装

做大屏的时候经常会遇到 echarts 展示,下面展示在 React (^18.2.0) 中对 echarts (^5.4.0) 的简单封装。

文章首发于https://blog.fxss.work/react/echarts封装.html样例查看

echarts 封装使用

props 说明

参数说明类型可选值默认值
opts初始化传入的 opts https://echarts.apache.org/zh/api.html#echarts.initObject-{renderer: 'svg'}
options配置项,对应 https://echarts.apache.org/zh/option.html#titleObject-{}
seriesDataseries 数据配置内容https://echarts.apache.org/zh/option.html#series,数据变更自动更新Array-[]
intervalTime自动切换的时间跨度,指自动切换 高亮 + tooltip 展示,例子Number-1500
autoPlay是否自动播放,指的是是否自动添加切换 高亮 + tooltip 展示Boolean-true
isAddOn是否自动添加鼠标上移事件,切换 高亮 + tooltip 展示的时候,鼠标再移动到其他需要高亮显示上时,自动停止切换动画,鼠标移开自动继续播放Boolean-true
onRefref实例使用,在父组件中const echartsRef = React.createRef();...<EchartsModule onRef={echartsRef} />---
className添加样式String-''

方法

方法名说明参数
echartsInstance返回 echarts 实例,如果功能不满足,自己定义-
echartsPlayecharts开启动画,对外开放,可手动调用clear = false, seriesIndex = 0, dataIndex = -1clear: 是否立即开始动画,并清除上个定时器,开启下个定时器,默认为 false;seriesIndex: series 中的第几项数据,默认为 0;dataIndex: series[seriesIndex].data 中的第几项,默认为 -1
echartsPauseecharts关闭动画,对外开放,可手动调用-

使用

如下演示 echarts 封装使用:

可以将如下代码拷贝到项目运行,更方便查看效果

import { Button, Typography, theme } from "antd";
import React from "react";
import EchartsModule from "../../components/EchartsModule";

const { Title } = Typography;
const { useToken } = theme;

const PageDemo = () => {
  const { token } = useToken();
  const { colorText, colorBgContainer, colorBorder } = token;
  const echartsRef = React.createRef();
  const options = {
    textStyle: {
      color: colorText,
    },
    title: {
      text: '饼图程序调用高亮示例',
      left: 'center',
      textStyle: {
        color: colorText,
      },
    },
    tooltip: {
      trigger: 'item',
      formatter: '{a} <br/>{b} : {c} ({d}%)',
      confine: true,
      className: 'echart-tooltip-zIndex',
      backgroundColor: colorBgContainer,
      borderColor: colorBorder,
      textStyle: {
        color: colorText,
      },
    },
    legend: {
      orient: 'vertical',
      left: 'left',
      data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎'],
      textStyle: {
        color: colorText,
      },
    }
  }

  const seriesData = [
    {
      name: '访问来源',
      type: 'pie',
      radius: '55%',
      center: ['50%', '60%'],
      lable: {
        textStyle: {
          color: colorText,
        },
      },
      data: [
        { value: 335, name: '直接访问' },
        { value: 310, name: '邮件营销' },
        { value: 234, name: '联盟广告' },
        { value: 135, name: '视频广告' },
        { value: 1548, name: '搜索引擎' }
      ],
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
        }
      }
    }
  ]
  const changeDate = () => {
    echartsRef.current.echartsPlay(true, 0, -1)
  }
  const changeDate1 = () => {
    echartsRef.current.echartsPause()
  }
  const changeDate2 = () => {
    const echartsInstance = echartsRef.current.echartsInstance()
    echartsInstance.clear()
    echartsInstance.dispose()
  }

  return (
    <div>
      <Title level={3}>6、通过 ref 调用开始结束动画,使用 ref 调用的好处是可以指定在第几项开始动画</Title>
      <div>
        <Button onClick={changeDate}>开始动画</Button>
        <Button className="ml-2" onClick={changeDate1}>结束动画</Button>
        <Button className="ml-2" onClick={changeDate2}>获取实例,销毁echarts</Button>
      </div>
      <div className="w-full h-80">
        <EchartsModule onRef={echartsRef} options={options} seriesData={seriesData}></EchartsModule>
      </div>
    </div>
  );
};

export default PageDemo;

代码封装

import { useEffect, useImperativeHandle, useRef } from "react";
import * as echarts from 'echarts';
import { useDeepCompareEffect, useMount, useSize, useUnmount, useUpdateEffect } from "ahooks";
import classNames from 'classnames';

const EchartsModule = ({
  // https://echarts.apache.org/zh/api.html#echarts.init
  // 初始化传入的 opts
  opts = { renderer: 'svg' },
  // 配置项
  options = {},
  // 数据集合
  seriesData = [],
  // 自动切换的时间跨度
  intervalTime = 1500,
  // 是否自动播放
  autoPlay = true,
  // 是否自动添加鼠标上移事件
  isAddOn = true,
  onRef,
  className = ''
}) => {
  
  const echartsRef = useRef(null);
  let myChart = useRef(null);
  let echartsOptions = useRef({});
  let myChartEventTime = useRef(null);
  let currentSeriesIndex = useRef(0);
  let currentDataIndex = useRef(-1);
  let timer = useRef(intervalTime);

  // 是否调用过 echartsPlay
  let isEchartsPlay = useRef(false);
  // echarts初始化
  function init() {
    destroyEchart() //判断是否有echart实例,如果有,先销毁
    myChart.current = echarts.init(echartsRef.current, null, opts)
    update()

    if (isAddOn) {
      addEventFn()
    }
  }

  // 绑定事件
  function addEventFn() {
    // 鼠标移上查看的时候,暂停动画
    myChart.current.on('mouseover', 'series', event => {
      // 取消之前高亮的图形
      myChart.current.dispatchAction({
        type: 'downplay',
        seriesIndex: currentSeriesIndex.current,
        dataIndex: currentDataIndex.current
      })
      echartsPause()
    })
    // 鼠标移出的时候打开动画
    myChart.current.on('mouseout', 'series', event => {
      // 自动播放 或者 调用过 echartsPlay
      if (autoPlay || isEchartsPlay.current) echartsPlay(true, event.seriesIndex, event.dataIndex - 1)
    })
  }

  // 移除事件
  function removeEventFn() {
    myChart.current.off('mouseover')
    myChart.current.off('mouseout')
  }

  // 数据更新
  function update() {
    // 逻辑处理组件options参数
    const curOptions = {
      ...options,
      series: seriesData
      // other options here ...
    }
    echartsOptions.current = curOptions
    // 调用ECharts组件setOption更新
    myChart.current.setOption(curOptions, true)
    if (curOptions.series.length && autoPlay) {
      myChart.current.dispatchAction({
        type: 'highlight',
        seriesIndex: currentSeriesIndex.current,
        dataIndex: currentDataIndex.current
      })
      // 显示 tooltip
      myChart.current.dispatchAction({
        type: 'showTip',
        seriesIndex: currentSeriesIndex.current,
        dataIndex: currentDataIndex.current
      })
      echartsPlay(false, currentSeriesIndex.current, currentDataIndex.current <= seriesData[currentSeriesIndex.current].data.length - 1 ? currentDataIndex.current : -1)
    }
  }

  // 销毁echarts
  function destroyEchart() {
    if (myChart.current) {
      if (isAddOn) {
        removeEventFn()
      }
      if (typeof myChart.current.clear === 'function') myChart.current.clear()
      if (typeof myChart.current.dispose === 'function') myChart.current.dispose()
      myChart.current = null
    }
  }

  /**
   * echarts开启动画,可手动调用
   * clear: 是否立即开始动画,并清除上个定时器,开启下个定时器,默认为 false
   * seriesIndex: series 中的第几项数据,默认为 0
   * dataIndex: series[seriesIndex].data 中的第几项,默认为 -1
   */
  function echartsPlay(clear = false, seriesIndex = 0, dataIndex = -1) {
    if (clear) {
      echartsPause()
    }
    isEchartsPlay.current = true
    currentSeriesIndex.current = seriesIndex
    currentDataIndex.current = dataIndex
    if (!myChartEventTime.current) {
      echartsEventPlay(seriesIndex)
    }
  }

  function echartsTimeout(seriesIndex = 0) {
    myChartEventTime.current = setTimeout(() => {
      echartsEventPlay(seriesIndex)
    }, timer.current)
  }

  function echartsEventPlay(seriesIndex = 0) {
    const dataLen = echartsOptions.current.series[seriesIndex].data.length
    if (myChart.current && myChart.current.dispatchAction) {
      // 取消之前高亮的图形
      myChart.current.dispatchAction({
        type: 'downplay',
        seriesIndex,
        dataIndex: currentDataIndex.current
      })
      currentDataIndex.current = (currentDataIndex.current + 1) % dataLen
      // 高亮当前图形
      myChart.current.dispatchAction({
        type: 'highlight',
        seriesIndex,
        dataIndex: currentDataIndex.current
      })
      // 显示 tooltip
      myChart.current.dispatchAction({
        type: 'showTip',
        seriesIndex,
        dataIndex: currentDataIndex.current
      })
    }
    echartsTimeout(seriesIndex)
  }

  // echarts关闭动画,可手动调用
  function echartsPause() {
    if (myChart.current && myChart.current.dispatchAction) {
      // 取消之前高亮的图形
      myChart.current.dispatchAction({
        type: 'downplay',
        seriesIndex: currentSeriesIndex.current,
        dataIndex: currentDataIndex.current
      })
      // 取消显示 tooltip
      myChart.current.dispatchAction({
        type: 'hideTip'
      })
    }
    if (myChartEventTime.current) {
      clearTimeout(myChartEventTime.current)
      myChartEventTime.current = null
    }
  }

  // 重置大小
  const echartsResize = () => {
    if (myChart.current) myChart.current.resize()
  }

  useMount(() => {
    init()
  })
  useUnmount(() => {
    echartsPause()
    destroyEchart()
  })

  useDeepCompareEffect(() => {
    update()
  }, [seriesData])

  useUpdateEffect(() => {
    if (autoPlay) {
      echartsPlay(false, currentSeriesIndex.current, currentDataIndex.current)
    } else {
      echartsPause()
    }
  }, [autoPlay])

  useUpdateEffect(() => {
    timer.current = intervalTime
  }, [intervalTime])

  useUpdateEffect(() => {
    if (isAddOn) {
      addEventFn()
    } else {
      removeEventFn()
    }
  }, [isAddOn])

  const size = useSize(echartsRef)
  useEffect(() => {
    echartsResize()
  }, [size])

  useImperativeHandle(onRef, () => {
    return {
      echartsInstance: () => myChart.current,
      echartsPlay,
      echartsPause
    }
  });

  return (
    <div ref={echartsRef} className={classNames('w-full h-full', className)}></div>
  );
};

export default EchartsModule;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值