react使用Chart

386 阅读2分钟

react使用Chart

截屏2023-05-09 10.45.56.png

安装

npm install @antv/g2 --save 

使用

image.png

数据处理

截屏2023-05-09 11.01.10.png

鼠标指上去是否展示辅助线

截屏2023-05-09 11.03.26.png

设置图形每个通道的比例尺

截屏2023-05-09 11.09.22.png

设置图形的坐标轴

截屏2023-05-09 11.09.42.png

销毁

截屏2023-05-09 11.10.27.png

完整代码


import React, { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
import { Popover } from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Chart } from '@antv/g2';
import styles from './index.less';

let chart:any;
const LineChart = forwardRef(({ lineList }: any,ref) => {
    useImperativeHandle(ref, () => ({
        handleClear,
    }));
    useEffect(() => {
        if (lineList) {
            let arr: any[] = [];
            lineList.forEach((element: any) => {
                let obj = {
                    periodDate: element.periodDate,
                    temperature: element.idxPosAmtFnl?element.idxPosAmtFnl/100:0, // 销售额
                    name: '销售额',
                };
                let item = {
                    periodDate: element.periodDate,
                    temperature: element.idxPosCntBsk, // 订单数
                    name: '订单数',
                };
                arr.push(obj);
                arr.push(item);
            });
            chart = new Chart({
                container: 'lineChart',
                autoFit: true,
                height: 100,
            });
            chart.data(arr);
            chart.scale({
                periodDate: {
                    range: [0, 1],
                },
                temperature: {
                    nice: true,
                },
            });

            chart.tooltip({
                showCrosshairs: true,
                shared: true,
            });

            chart.axis('temperature', {
                label: {
                    formatter: (val:string) => {
                        return val;
                    },
                },
            });

            chart.line().position('periodDate*temperature').color('name').shape('smooth');

            chart.point().position('periodDate*temperature').color('name').shape('circle');
            chart.legend(false); //关闭底部内容
            chart.render();
        }
    }, [lineList]);
    const handleClear = ()=>{
      !!chart && chart?.destroy();
      chart = null;
    }
    return (
        <div className={styles.lineChart}>
            <div className={styles.lineChartHead}>
                <div>
                    销售额/订单数
                    <Popover content="当前门店30天内的销售额和订单数趋势数据">
                        <ExclamationCircleOutlined />
                    </Popover>
                </div>
                <div>30天趋势图</div>
            </div>
            <div className={styles.chart}>
                <div id="lineChart"></div>
            </div>
        </div>
    );
});

export default LineChart;

页面使用

  <LineChart ref={chartRef} lineList={data?.lineList} />

请求时销毁

const handleChartFn = ()=>{
    chartRef && chartRef?.current?.handleClear();
}

问题

请求之后出现重叠问题

企业微信截图_16832477738414.png 解决:请求之后没有销毁导致又创建了一个chart,每次请求的时候判断有chart就销毁。

截屏2023-05-09 11.16.19.png 使用react的forwardRef方法,在父组件请求时调用销毁方法

截屏2023-05-09 11.17.37.png 没有创建chart就销毁会报错。

!重大问题:

优化: 我的代码场景是页面有个刷新按钮,在页面10分钟可以请求一次,结果上面的写法正确请求没有问题,在页面停留很长一段时间后会导致饼图出现重影,折线图没有问题。上面的代码还在函数外定义了chart结果,我在useEffect里又定义了一个chart,所以导致chart没被销毁

Pasted Graphic 4.png

优化代码:使用useRef

const StatisticsCard.png

useEffect(().png

1 默认择.png

edv classNamea(styles.container ) refa(containerRef).png

饼图代码:

import React, { useEffect, useImperativeHandle, forwardRef, useRef } from 'react';
import { Card, Popover } from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Chart } from '@antv/g2';
import styles from './index.less';

const StatisticsCard = forwardRef(
    ({ title, id, unit, total = 0, num = 0, online = 0, offline = 0, todayMembers = 0, tips = '' }: any, ref) => {
        // 使用ref获取容器元素
        const containerRef:any = useRef(null);
        useImperativeHandle(ref, () => ({
            handleClear,
        }));

        useEffect(() => {
            if (id == 3) {
                return;
            }
            const data = [
                { item: '线上', percent: online },
                { item: '线下', percent: offline },
            ];
            const chart = new Chart({
                container: containerRef.current,
                autoFit: true,
            });
            chart.data(data);

            chart.coordinate('theta', {
                radius: 0.85,
            });
            chart.scale('percent', {
                formatter: (val) => {
                    return val;
                },
            });
            chart.tooltip({
                showTitle: false,
                showMarkers: false,
            });
            chart.axis(false); // 关闭坐标轴
            const interval = chart
                .interval()
                .adjust('stack')
                .position('percent')
                .color('item')
                .label('item', (val) => {
                    if (!!online && !!offline) {
                        return {
                            offset: -15,
                            style: {
                                opacity: 1,
                                fill: 'white',
                                fontSize: 12,
                                shadowBlur: 2,
                                shadowColor: 'rgba(0, 0, 0, .45)',
                            },
                            content: (obj) => {
                                // if (!!obj.percent) {
                                //     return obj.item + '\n' + obj.percent;
                                // }
                                return '';
                            },
                        };
                    } else {
                        return {
                            // offset: -50,
                            offsetY: -50,
                            style: {
                                textAlign: 'center',
                                opacity: 1,
                                fill: 'white',
                                fontSize: 12,
                                shadowBlur: 2,
                                shadowColor: 'rgba(0, 0, 0, .45)',
                            },
                            content: (obj) => {
                                // if (!!obj.percent) {
                                //     return obj.item + '\n' + obj.percent;
                                // }
                                return '';
                            },
                        };
                    }
                })
                .tooltip('item*percent', (item, percent) => {
                    percent = percent + unit;
                    return {
                        name: item,
                        value: percent,
                    };
                })
                .style({
                    lineWidth: 1,
                    stroke: '#fff',
                });
            chart.interaction('element-single-selected');
            // chart.legend(false); //关闭底部内容
            chart.render();

            // 默认选择
            interval.elements[0].setState('selected', true);
            // 返回清理函数,在组件卸载时销毁图表实例,避免内存泄漏
            return () => {
                chart.destroy();
            };
        }, [offline]);
        const handleClear = () => {
            // console.log(chart, 'chart');
            // !!chart && chart?.destroy();
            // chart = null;
        };
        return (
            <Card className={styles.statisticsCard}>
                <div className={styles.head}>
                    <div>{title}</div>
                    <Popover content={tips}>
                        <ExclamationCircleOutlined />
                    </Popover>
                </div>
                <div className={id == 3 ? `${styles.content} ${styles.flex}` : `${styles.content}`}>
                    <div>
                        <div className={styles.num}>
                            {total}
                            {unit}
                        </div>
                        {id == 3 ? (
                            <div className={styles.mt10}>总会员数</div>
                        ) : (
                            <div className={styles.red}>
                                昨日全天:{num}
                                {unit}
                            </div>
                        )}
                    </div>
                    <div>
                        {id == 3 ? (
                            <div className={styles.contentRight}>
                                <div>
                                    {todayMembers}
                                    {unit}
                                </div>
                                <div className={styles.add}>今日新增数</div>
                                <div className={styles.redText}>
                                    昨日新增:{num}
                                    {unit}
                                </div>
                            </div>
                        ) : (
                            <div className={styles.container} ref={containerRef} id={`container${id}`}></div>
                        )}
                    </div>
                </div>
            </Card>
        );
    },
);

export default StatisticsCard;

Pasted Graphic 5.png 这样写法就不需要传递方法出去销毁chart了,还是多使用useRef吧