useECharts.ts 3.9 KB
Newer Older
P
Peter Pan 已提交
1
import {MutableRefObject, useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react';
P
Peter Pan 已提交
2
import {maskColor, position, primaryColor, size, textColor} from '~/utils/style';
3

4
import type {ECharts} from 'echarts';
P
Peter Pan 已提交
5 6
import {dataURL2Blob} from '~/utils/image';
import {saveAs} from 'file-saver';
P
Peter Pan 已提交
7
import styled from 'styled-components';
8

9
export type Options = {
P
Peter Pan 已提交
10
    loading?: boolean;
11
    gl?: boolean;
P
Peter Pan 已提交
12
    zoom?: boolean;
P
Peter Pan 已提交
13
    autoFit?: boolean;
14 15 16 17 18 19 20
    onInit?: (echarts: ECharts) => unknown;
    onDispose?: (echarts: ECharts) => unknown;
};

const useECharts = <T extends HTMLElement, W extends HTMLElement = HTMLDivElement>(
    options: Options
): {
P
Peter Pan 已提交
21
    ref: MutableRefObject<T | null>;
P
Peter Pan 已提交
22 23
    wrapper: MutableRefObject<W | null>;
    echart: ECharts | null;
P
Peter Pan 已提交
24
    saveAsImage: (filename?: string) => void;
P
Peter Pan 已提交
25
} => {
26 27
    const ref = useRef<T | null>(null);
    const echartInstance = useRef<ECharts | null>(null);
P
Peter Pan 已提交
28
    const [echart, setEchart] = useState<ECharts | null>(null);
29

30 31 32
    const onInit = useRef(options.onInit);
    const onDispose = useRef(options.onDispose);

33
    const createChart = useCallback(() => {
P
Peter Pan 已提交
34
        (async () => {
35
            const {default: echarts} = await import('echarts');
P
Peter Pan 已提交
36 37 38
            if (options.gl) {
                await import('echarts-gl');
            }
39 40 41
            if (!ref.current) {
                return;
            }
P
Peter Pan 已提交
42
            echartInstance.current = echarts.init((ref.current as unknown) as HTMLDivElement);
43 44 45

            setTimeout(() => {
                if (options.zoom) {
P
Peter Pan 已提交
46 47 48 49 50
                    echartInstance.current?.dispatchAction({
                        type: 'takeGlobalCursor',
                        key: 'dataZoomSelect',
                        dataZoomSelectActive: true
                    });
51 52 53 54 55 56 57
                }

                if (echartInstance.current) {
                    onInit.current?.(echartInstance.current);
                }
            }, 0);

P
Peter Pan 已提交
58
            setEchart(echartInstance.current);
P
Peter Pan 已提交
59 60
        })();
    }, [options.gl, options.zoom]);
61 62

    const destroyChart = useCallback(() => {
63 64 65
        if (echartInstance.current) {
            onDispose.current?.(echartInstance.current);
        }
P
Peter Pan 已提交
66 67
        echartInstance.current?.dispose();
        setEchart(null);
68 69 70
    }, []);

    useEffect(() => {
71 72
        createChart();
        return destroyChart;
73 74 75
    }, [createChart, destroyChart]);

    useEffect(() => {
76 77 78 79 80 81 82 83 84 85
        if (options.loading) {
            echartInstance.current?.showLoading('default', {
                text: '',
                color: primaryColor,
                textColor,
                maskColor,
                zlevel: 0
            });
        } else {
            echartInstance.current?.hideLoading();
86
        }
P
Peter Pan 已提交
87 88 89 90
    }, [options.loading]);

    const wrapper = useRef<W | null>(null);
    useLayoutEffect(() => {
91
        if (options.autoFit) {
P
Peter Pan 已提交
92 93 94 95 96 97 98 99 100 101
            const w = wrapper.current;
            if (w) {
                const observer = new ResizeObserver(() => {
                    echartInstance.current?.resize();
                });
                observer.observe(w);
                return () => observer.unobserve(w);
            }
        }
    }, [options.autoFit]);
102

P
Peter Pan 已提交
103 104 105 106 107 108 109 110 111 112 113
    const saveAsImage = useCallback(
        (filename?: string) => {
            if (echart) {
                const blob = dataURL2Blob(echart.getDataURL({type: 'png', pixelRatio: 2, backgroundColor: '#FFF'}));
                saveAs(blob, `${filename?.replace(/[/\\?%*:|"<>]/g, '_') || 'chart'}.png`);
            }
        },
        [echart]
    );

    return {ref, echart, wrapper, saveAsImage};
114 115 116
};

export default useECharts;
P
Peter Pan 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135

export const Wrapper = styled.div`
    position: relative;
    display: flex;
    justify-content: center;
    align-items: stretch;

    > .echarts {
        width: 100%;
    }

    > .loading {
        ${size('100%')}
        ${position('absolute', 0, null, null, 0)}
        display: flex;
        justify-content: center;
        align-items: center;
    }
`;