useECharts.ts 8.9 KB
Newer Older
P
Peter Pan 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/**
 * Copyright 2020 Baidu Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

P
Peter Pan 已提交
17 18
// cSpell:words zlevel

P
Peter Pan 已提交
19 20
import {MutableRefObject, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react';
import {position, primaryColor, size} from '~/utils/style';
21

22
import type {ECharts} from 'echarts';
P
Peter Pan 已提交
23
import {dataURL2Blob} from '~/utils/image';
P
Peter Pan 已提交
24
import {saveFile} from '~/utils/saveFile';
P
Peter Pan 已提交
25
import styled from 'styled-components';
P
Peter Pan 已提交
26 27
import {themes} from '~/utils/theme';
import useTheme from '~/hooks/useTheme';
28

29
export type Options = {
P
Peter Pan 已提交
30
    loading?: boolean;
31
    gl?: boolean;
P
Peter Pan 已提交
32
    zoom?: boolean;
P
Peter Pan 已提交
33
    autoFit?: boolean;
34 35 36 37 38 39 40
    onInit?: (echarts: ECharts) => unknown;
    onDispose?: (echarts: ECharts) => unknown;
};

const useECharts = <T extends HTMLElement, W extends HTMLElement = HTMLDivElement>(
    options: Options
): {
P
Peter Pan 已提交
41
    ref: MutableRefObject<T | null>;
P
Peter Pan 已提交
42 43
    wrapper: MutableRefObject<W | null>;
    echart: ECharts | null;
P
Peter Pan 已提交
44
    saveAsImage: (filename?: string) => void;
P
Peter Pan 已提交
45
} => {
46
    const ref = useRef<T | null>(null);
P
Peter Pan 已提交
47
    const [echart, setEchart] = useState<ECharts | null>(null);
P
Peter Pan 已提交
48
    const theme = useTheme();
49

50 51 52
    const onInit = useRef(options.onInit);
    const onDispose = useRef(options.onDispose);

P
Peter Pan 已提交
53
    const hideTip = useCallback(() => echart?.dispatchAction({type: 'hideTip'}), [echart]);
54

55
    const createChart = useCallback(() => {
P
Peter Pan 已提交
56
        (async () => {
P
Peter Pan 已提交
57 58 59 60
            if (!ref.current) {
                return;
            }

61
            const {default: echarts} = await import('echarts');
P
Peter Pan 已提交
62 63 64
            if (options.gl) {
                await import('echarts-gl');
            }
P
Peter Pan 已提交
65

P
Peter Pan 已提交
66
            const echartInstance = echarts.init(ref.current as unknown as HTMLDivElement);
67

68 69
            ref.current.addEventListener('mouseleave', hideTip);

70 71
            setTimeout(() => {
                if (options.zoom) {
P
Peter Pan 已提交
72
                    echartInstance.dispatchAction({
P
Peter Pan 已提交
73 74 75 76
                        type: 'takeGlobalCursor',
                        key: 'dataZoomSelect',
                        dataZoomSelectActive: true
                    });
77 78
                }

P
Peter Pan 已提交
79 80
                if (echartInstance) {
                    onInit.current?.(echartInstance);
81 82 83
                }
            }, 0);

P
Peter Pan 已提交
84
            setEchart(echartInstance);
P
Peter Pan 已提交
85
        })();
86
    }, [options.gl, options.zoom, hideTip]);
87 88

    const destroyChart = useCallback(() => {
P
Peter Pan 已提交
89 90 91 92 93
        if (echart) {
            onDispose.current?.(echart);
            echart.dispose();
            ref.current?.removeEventListener('mouseleave', hideTip);
            setEchart(null);
94
        }
P
Peter Pan 已提交
95
    }, [hideTip, echart]);
96 97

    useEffect(() => {
98 99
        createChart();
        return destroyChart;
100 101 102
    }, [createChart, destroyChart]);

    useEffect(() => {
103
        if (options.loading) {
P
Peter Pan 已提交
104
            echart?.showLoading('default', {
105 106
                text: '',
                color: primaryColor,
P
Peter Pan 已提交
107 108
                textColor: themes[theme].textColor,
                maskColor: themes[theme].maskColor,
109 110 111
                zlevel: 0
            });
        } else {
P
Peter Pan 已提交
112
            echart?.hideLoading();
113
        }
P
Peter Pan 已提交
114
    }, [options.loading, theme, echart]);
P
Peter Pan 已提交
115 116 117

    const wrapper = useRef<W | null>(null);
    useLayoutEffect(() => {
118
        if (options.autoFit) {
P
Peter Pan 已提交
119 120
            const w = wrapper.current;
            if (w) {
P
Peter Pan 已提交
121
                let animationId: number | null = null;
P
Peter Pan 已提交
122
                const observer = new ResizeObserver(() => {
P
Peter Pan 已提交
123 124 125 126 127 128 129
                    if (animationId != null) {
                        cancelAnimationFrame(animationId);
                        animationId = null;
                    }
                    animationId = requestAnimationFrame(() => {
                        echart?.resize();
                    });
P
Peter Pan 已提交
130 131 132 133 134
                });
                observer.observe(w);
                return () => observer.unobserve(w);
            }
        }
P
Peter Pan 已提交
135
    }, [options.autoFit, echart]);
136

P
Peter Pan 已提交
137 138 139 140
    const saveAsImage = useCallback(
        (filename?: string) => {
            if (echart) {
                const blob = dataURL2Blob(echart.getDataURL({type: 'png', pixelRatio: 2, backgroundColor: '#FFF'}));
P
Peter Pan 已提交
141
                saveFile(blob, `${filename || 'chart'}.png`);
P
Peter Pan 已提交
142 143 144 145 146 147
            }
        },
        [echart]
    );

    return {ref, echart, wrapper, saveAsImage};
148 149 150
};

export default useECharts;
P
Peter Pan 已提交
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169

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;
    }
`;
P
Peter Pan 已提交
170 171 172 173

export const useChartTheme = (gl?: boolean) => {
    const theme = useTheme();
    const tt = useMemo(() => themes[theme], [theme]);
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
    const result = useMemo(() => {
        if (gl) {
            return {
                title: {
                    textStyle: {
                        color: tt.textColor
                    }
                },
                tooltip: {
                    backgroundColor: tt.tooltipBackgroundColor,
                    borderColor: tt.tooltipBackgroundColor,
                    textStyle: {
                        color: tt.tooltipTextColor
                    }
                },
                xAxis3D: {
                    nameTextStyle: {
P
Peter Pan 已提交
191
                        color: tt.textLightColor
192 193
                    },
                    axisLabel: {
P
Peter Pan 已提交
194
                        color: tt.textLightColor
195 196 197 198 199 200 201 202 203 204 205 206 207 208
                    },
                    axisLine: {
                        lineStyle: {
                            color: tt.borderColor
                        }
                    },
                    splitLine: {
                        lineStyle: {
                            color: tt.borderColor
                        }
                    }
                },
                yAxis3D: {
                    nameTextStyle: {
P
Peter Pan 已提交
209
                        color: tt.textLightColor
210 211
                    },
                    axisLabel: {
P
Peter Pan 已提交
212
                        color: tt.textLightColor
213 214 215 216 217 218 219 220 221 222 223 224 225 226
                    },
                    axisLine: {
                        lineStyle: {
                            color: tt.borderColor
                        }
                    },
                    splitLine: {
                        lineStyle: {
                            color: tt.borderColor
                        }
                    }
                },
                zAxis3D: {
                    nameTextStyle: {
P
Peter Pan 已提交
227
                        color: tt.textLightColor
228 229
                    },
                    axisLabel: {
P
Peter Pan 已提交
230
                        color: tt.textLightColor
231 232 233 234 235 236 237 238 239 240 241 242 243 244
                    },
                    axisLine: {
                        lineStyle: {
                            color: tt.borderColor
                        }
                    },
                    splitLine: {
                        lineStyle: {
                            color: tt.borderColor
                        }
                    }
                }
            };
        }
P
Peter Pan 已提交
245 246 247 248 249 250 251 252 253 254 255 256 257
        return {
            title: {
                textStyle: {
                    color: tt.textColor
                }
            },
            tooltip: {
                backgroundColor: tt.tooltipBackgroundColor,
                borderColor: tt.tooltipBackgroundColor,
                textStyle: {
                    color: tt.tooltipTextColor
                }
            },
258
            xAxis: {
P
Peter Pan 已提交
259
                nameTextStyle: {
P
Peter Pan 已提交
260
                    color: tt.textLightColor
P
Peter Pan 已提交
261 262
                },
                axisLabel: {
P
Peter Pan 已提交
263
                    color: tt.textLightColor
P
Peter Pan 已提交
264 265 266 267 268 269 270 271 272 273 274 275
                },
                axisLine: {
                    lineStyle: {
                        color: tt.borderColor
                    }
                },
                splitLine: {
                    lineStyle: {
                        color: tt.borderColor
                    }
                }
            },
276
            yAxis: {
P
Peter Pan 已提交
277
                nameTextStyle: {
P
Peter Pan 已提交
278
                    color: tt.textLightColor
P
Peter Pan 已提交
279 280
                },
                axisLabel: {
P
Peter Pan 已提交
281
                    color: tt.textLightColor
P
Peter Pan 已提交
282 283 284 285 286 287 288 289 290 291 292 293 294
                },
                axisLine: {
                    lineStyle: {
                        color: tt.borderColor
                    }
                },
                splitLine: {
                    lineStyle: {
                        color: tt.borderColor
                    }
                }
            }
        };
295 296
    }, [tt, gl]);
    return result;
P
Peter Pan 已提交
297
};