|
| 1 | +// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。 |
| 2 | +import * as echarts from 'echarts/core'; |
| 3 | +// 引入柱状图图表,图表后缀都为 Chart |
| 4 | +import { LineChart } from 'echarts/charts'; |
| 5 | +// 引入标题,提示框,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component |
| 6 | +import { |
| 7 | + DatasetComponent, |
| 8 | + DataZoomComponent, |
| 9 | + GraphicComponent, |
| 10 | + GridComponent, |
| 11 | + LegendComponent, |
| 12 | + TitleComponent, |
| 13 | + TooltipComponent, |
| 14 | + TransformComponent, |
| 15 | + MarkLineComponent, |
| 16 | + MarkPointComponent, |
| 17 | +} from 'echarts/components'; |
| 18 | +// 标签自动布局、全局过渡动画等特性 |
| 19 | + |
| 20 | +import { LabelLayout, UniversalTransition } from 'echarts/features'; |
| 21 | +// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步 |
| 22 | +import { CanvasRenderer } from 'echarts/renderers'; |
| 23 | +import { useColorMode } from '@docusaurus/theme-common'; |
| 24 | + |
| 25 | +// 注册必须的组件 |
| 26 | +echarts.use([ |
| 27 | + LineChart, |
| 28 | + GraphicComponent, |
| 29 | + TitleComponent, |
| 30 | + TooltipComponent, |
| 31 | + LegendComponent, |
| 32 | + GridComponent, |
| 33 | + DatasetComponent, |
| 34 | + TransformComponent, |
| 35 | + LegendComponent, |
| 36 | + LabelLayout, |
| 37 | + UniversalTransition, |
| 38 | + CanvasRenderer, |
| 39 | + DataZoomComponent, |
| 40 | + MarkLineComponent, |
| 41 | + MarkPointComponent, |
| 42 | +]); |
| 43 | + |
| 44 | +import { useEffect, useRef } from 'react'; |
| 45 | + |
| 46 | +const EchartsView = (props) => { |
| 47 | + const { option, className, style, onEvents = {}, onInit } = props; |
| 48 | + const echartsRef = useRef(null); |
| 49 | + const chartInstanceRef = useRef(null); |
| 50 | + const { colorMode } = useColorMode(); |
| 51 | + |
| 52 | + useEffect(() => { |
| 53 | + // 确保 DOM 元素存在且有尺寸 |
| 54 | + if (!echartsRef.current) return; |
| 55 | + // 检查容器是否有尺寸 |
| 56 | + const container = echartsRef.current; |
| 57 | + if (container.clientWidth === 0 || container.clientHeight === 0) { |
| 58 | + // 如果容器没有尺寸,使用 ResizeObserver 监听尺寸变化 |
| 59 | + const resizeObserver = new ResizeObserver((entries) => { |
| 60 | + for (let entry of entries) { |
| 61 | + if (entry.contentRect.width > 0 && entry.contentRect.height > 0) { |
| 62 | + // 容器有尺寸了,初始化图表 |
| 63 | + initializeChart(); |
| 64 | + resizeObserver.disconnect(); |
| 65 | + break; |
| 66 | + } |
| 67 | + } |
| 68 | + }); |
| 69 | + |
| 70 | + resizeObserver.observe(container); |
| 71 | + // 添加一个超时机制作为后备方案 |
| 72 | + const timeoutId = setTimeout(() => { |
| 73 | + resizeObserver.disconnect(); |
| 74 | + if (container.clientWidth > 0 || container.clientHeight > 0) { |
| 75 | + initializeChart(); |
| 76 | + } |
| 77 | + }, 100); |
| 78 | + |
| 79 | + return () => { |
| 80 | + resizeObserver.disconnect(); |
| 81 | + clearTimeout(timeoutId); |
| 82 | + }; |
| 83 | + } else { |
| 84 | + // 容器已经有尺寸,直接初始化 |
| 85 | + initializeChart(); |
| 86 | + } |
| 87 | + function initializeChart() { |
| 88 | + // 如果已经有图表实例,先销毁 |
| 89 | + if (chartInstanceRef.current) { |
| 90 | + chartInstanceRef.current.dispose(); |
| 91 | + } |
| 92 | + |
| 93 | + // 初始化图表 |
| 94 | + chartInstanceRef.current = echarts.init(echartsRef.current, colorMode); |
| 95 | + if (!chartInstanceRef.current) return; |
| 96 | + |
| 97 | + chartInstanceRef.current.setOption(option); |
| 98 | + |
| 99 | + if (onInit && typeof onInit === 'function') { |
| 100 | + onInit(chartInstanceRef.current); |
| 101 | + } |
| 102 | + // 绑定事件 |
| 103 | + Object.keys(onEvents).forEach((eventName) => { |
| 104 | + chartInstanceRef.current.on(eventName, (params) => { |
| 105 | + onEvents[eventName](params, chartInstanceRef.current); |
| 106 | + }); |
| 107 | + }); |
| 108 | + } |
| 109 | + |
| 110 | + // 添加窗口变化事件监听器 |
| 111 | + const resizeHandler = () => { |
| 112 | + if (chartInstanceRef.current) { |
| 113 | + chartInstanceRef.current.resize(); |
| 114 | + } |
| 115 | + }; |
| 116 | + |
| 117 | + window.addEventListener('resize', resizeHandler); |
| 118 | + |
| 119 | + return () => { |
| 120 | + // 移除所有事件监听器 |
| 121 | + Object.keys(onEvents).forEach((eventName) => { |
| 122 | + if (chartInstanceRef.current) { |
| 123 | + chartInstanceRef.current.off(eventName); |
| 124 | + } |
| 125 | + }); |
| 126 | + // 移除窗口变化事件监听器 |
| 127 | + window.removeEventListener('resize', resizeHandler); |
| 128 | + |
| 129 | + // 销毁图表实例 |
| 130 | + if (chartInstanceRef.current) { |
| 131 | + chartInstanceRef.current.dispose(); |
| 132 | + chartInstanceRef.current = null; |
| 133 | + } |
| 134 | + }; |
| 135 | + }, [option, colorMode]); |
| 136 | + |
| 137 | + return <div ref={echartsRef} className={className} style={style}></div>; |
| 138 | +}; |
| 139 | + |
| 140 | +export default EchartsView; |
0 commit comments