Antv G2(bizCharts) 点线图 实现

    技术2022-07-14  88

    名字

    点线图: 不知道怎么命名

    效果图

    思路

    数据: 初始化三条数据, 中间的为显示数据 使用折线图,隐藏折线,隐藏显示数据之外的所有点 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;
    Processed: 0.013, SQL: 10