Antv G2(bizCharts) 点线图 实现

名字

点线图: 不知道怎么命名

效果图

Antv G2(bizCharts) 点线图 实现_第1张图片

思路

数据: 初始化三条数据, 中间的为显示数据
使用折线图,隐藏折线,隐藏显示数据之外的所有点
X轴:隐藏X轴坐标内容
Y轴:设置y轴内容,y轴标题
注释:添加注释,线条,描述内容

代码(下面有全的子组件代码)

// 1、导入Chart图表
import { Chart } from "@antv/g2";
// 2、初始化图表
useEffect(() => {
    (async () => {
        const chartBox = new Chart({
            container: 'c3',
            autoFit: true,
            width: width,
            height: height,
            title: true
        })
        await setChartBox(chartBox) // 设置图表 chart
        const newData = cloneDeep(cData) // 复制数据 useState引用数据用新的地址替换
        newData.splice(1,1, data) // props接收到的显示的那条数据内容
        await setData(() => newData) // 设置新数据
    })()
}, [])
// 3、图表渲染
const setChart = () => {
    const margin = 1 / 3 // 3为数据列表长度
    chart.clear()
    chart.data(cData);
    chart.scale({
        date: {
        	// 坐标轴起始位置修改  一般[0 , 1]
            range: [margin / 2, 1 - margin / 2],
        },
        num: {
        	// 起始位置不变 末尾位置留白 给标题挤出空间
            range: [0, 1 - margin / 2],
            min: 0, // 设置坐标轴最小值
            max: max,// 设置坐标轴最大值
            nice: true, // 不设置最大值最小值的时候 会根据数据内容自动变化
        },
    });

    chart.tooltip(false); // 关闭提示

    // 坐标轴 X
    chart.axis('date', {
        label: {
            // 返回空字符串 隐藏横坐标文字内容
            formatter: (text) => ""
        }
    })
    // 坐标轴 Y
    chart.axis('num', {
        title: {
            text: cTitle, // 标题内容
            autoRotate: false,
            position: "end",
            offset: -10,
            textStyle: {
                textAlign: 'start', // 文本对齐方向,可取值为: start middle end
                fontSize: '12', // 文本大小
                fontWeight: 'bold', // 文本粗细
                textBaseline: 'top' // 文本基准线,可取 top middle bottom,默认为middle
            },
        },
        label: {
            formatter: (text) => text
        }
    })
    // 隐藏折线
    chart.line().style({
        stroke: "transparent" // 设置透明颜色
    }).position('date*num');
    // 隐藏点
    chart
        .point()
        .size(2) // 控制点的大小
        .position('date*num')
        .label("name", (val) => {
            // 关联文字 设置样式
            if (val) {
            	// 吐槽文本内容字段 一会text 一会content 全是试出来的
                return {
                	content: val, // 格式化文字 不给默认显示关联字段的值
                    offsetX: 12, // 距离设置点的X偏移量
                    offsetY: 12, // 距离设置点的Y偏移量
                    style: {
                        fill: dataColor,
                        fontSize: 12,
                    },
                }
            }
        })
        .style("num*name", ((val, name) => {
            // 只显示有名字的那个点 其他点隐藏
            if (name) {
                return {
                    stroke: dataColor,
                    fill: dataColor
                }
            }
            // 其他点 隐藏
            return {
                stroke: "transparent",
                lineWidth: 0
            }
        }))
    // 注释
    chart.annotation().line({
        top: true,
        start: [-0.5, averageNum], // 起始位置 第一个参数为X起始位置  第二个为Y
        end: [2.5, averageNum], // 结束位置 第一个参数为X起始位置  第二个为Y
        // 上面X轴通过 改变了起始位置 range: [margin / 2, 1 - margin / 2]
        // 现在又想要注释的横线横穿整个图表 所以向左右扩展
        // -0.5 向左扩展 2.5向右扩展  数字可以想成是数据列表的索引
        style: {
            stroke: contentColor,
            lineWidth: 1,
            lineDash: [3, 3],
        },
        text: {
            position: 'start',
            style: {
                fill: contentColor,
                fontSize: 12,
                fontWeight: 500,
            },
            content: content,
            offsetY: -5,
        },
    });

    chart.render();
}

子组件代码

import React, { useState, useEffect, useRef } from "react";
import "./chart.less"
import { Chart } from "@antv/g2";
import { cloneDeep } from "lodash"
import PropTypes from 'prop-types';

const Charts = (props) => {
    const {
        width,
        height,
        max,
        content,
        contentColor,
        dataColor,
        cTitle,
        data,
        averageNum
    } = props
    const initData = [
        { date: "a", num: 0 },
        { date: "b", num: 0 },
        { date: "c", num: 0 }
    ]
    const [cData, setData] = useState(initData)
    const [chart, setChartBox] = useState(null)
    const chartRef = useRef()

    useEffect(() => {
        (async () => {
            const chartBox = new Chart({
                container: 'c3',
                autoFit: true,
                width: width,
                height: height,
                title: true
            })
            await setChartBox(chartBox)
            const newData = cloneDeep(cData)
            newData.splice(1,1, data)
            await setData(() => newData)
        })()
    }, [])
    // 下面两个useEffect 根据传入内容改变 进行修改
    // 原来写法是这样的 不过现在根据react的dom diff的逻辑 在父组件使用时候传入个key值
    // 要更新的时候 修改下那个key就行了 少量操作问题不大
    // 大量的话 对性能不友好
    useEffect(() => {
    	// initData 初始固定三条数据  修改中间的即可
        if (cData[1].name && chart) {
            chart.clear()
            setChart()
        }
    }, [chart, cData])
	
    useEffect(() => {
        if (cTitle && chart) {
            chart.clear()
            setChart()
        }
    }, [chart, cTitle])

    useEffect(() => {
        (async () => {
            const newData = cloneDeep(cData)
            newData.splice(1,1, data)
            await setData(() => newData)
            if (data && chart) setChart()
        })()
    }, [chart, data])

    const setChart = () => {
        const margin = 1 / 3
        chart.clear()
        chart.data(cData);
        chart.scale({
            date: {
                range: [margin / 2, 1 - margin / 2],
            },
            num: {
                range: [0, 1 - margin / 2],
                min: 0,
                max: max,
                nice: true,
            },
        });

        chart.tooltip(false);

        // 坐标轴
        chart.axis('date', {
            label: {
                // 返回空字符串 隐藏横坐标文字内容
                formatter: (text) => ""
            }
        })
        chart.axis('num', {
            title: {
                text: cTitle,
                autoRotate: false,
                position: "end",
                offset: -10,
                textStyle: {
                    textAlign: 'start', // 文本对齐方向,可取值为: start middle end
                    fontSize: '12', // 文本大小
                    fontWeight: 'bold', // 文本粗细
                    textBaseline: 'top' // 文本基准线,可取 top middle bottom,默认为middle
                },
            },
            label: {
                formatter: (text) => text
            }
        })
        // 隐藏折线
        chart.line().style({
            stroke: "transparent"
        }).position('date*num');
        // 隐藏点
        chart
            .point()
            .size(2)
            .position('date*num')
            .label("name", (val) => {
                // 关联文字 设置样式
                if (val) {
                    return {
                        content: val,
                        offsetX: 12,
                        offsetY: 12,
                        style: {
                            fill: dataColor,
                            fontSize: 12,
                        },
                    }
                }
            })
            .style("num*name", ((val, name) => {
                // 只显示有名字的那个点 其他点隐藏
                if (name) {
                    return {
                        stroke: dataColor,
                        fill: dataColor
                    }
                }
                return {
                    stroke: "transparent",
                    lineWidth: 0
                }
            }))
        // 没找到清空annotation的方法 不用key触发更新的话
        // 再次渲染的时候会叠加出现上次的线
        chart.annotation().line({
            top: true,
            start: [-0.5, averageNum],
            end: [2.5, averageNum],
            style: {
                stroke: contentColor,
                lineWidth: 1,
                lineDash: [3, 3],
            },
            text: {
                position: 'start',
                style: {
                    fill: contentColor,
                    fontSize: 12,
                    fontWeight: 500,
                },
                content: content,
                offsetY: -5,
            },
        });

        chart.render();
    }

    return (
        <div className={"chart"}>
            <div id="c3" ref={chartRef} />
        </div>
    )
}
Charts.defaultProps = {
    max: 150,
    data: { date: "c", num: 60, name: "我" },
    width: 300,
    height: 200,
    dataColor: "#fe8140", // 文字 点颜色
    contentColor: "#f4a240", // 横线颜色
    cTitle: "单位: 小时", // 纵坐标轴标题
    content: "团队均值: 75",// 均值说明
    averageNum: 75,// 均值说明
}

Charts.propTypes = {
    max: PropTypes.number,
    data: PropTypes.object.isRequired,
    width: PropTypes.number,
    height: PropTypes.number,
    dataColor: PropTypes.string,
    contentColor: PropTypes.string,
    cTitle: PropTypes.string,
    content: PropTypes.string,
    averageNum: PropTypes.number,
}

export default Charts;

父组件代码

import React from 'react';
import { Button } from "antd"
import Chart3 from "./components/chart3"

class Charts extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            cTitle: "单位: 小时",
            data: { date: "b", num: 120, name: "我" },
            averageNum: 75
        }
    }

    change = () => {
        const { cTitle } = this.state
        const flag = cTitle === "单位: 个"
        const title = flag ? "单位: 小时" : "单位: 个"
        const data = flag ?
            { date: "b", num: 120, name: "我" } :
            { date: "b", num: 55, name: "你" }
        const averageNum = flag ? 75 : 55
        this.setState({
            cTitle: title,
            data: data,
            averageNum: averageNum,
        })
    }

    render () {
        const { cTitle, data, averageNum } = this.state
        // 组件通过key值变化可以触发更新
        return (
            <div className={"chartBox"}> 
                <Chart3
                    cTitle={cTitle}
                    data={data}
                    key={data.num}
                    averageNum={averageNum}
                />
                <Button onClick={this.change}>切换</Button>
            </div>
        )
    }
}

export default Charts;


你可能感兴趣的:(antv,G2,react,图表)