使用G2的BizChar 踩过的坑

3,924 阅读7分钟
新增next.js使用踩坑
juejin.cn/post/684490…

1.给柱状图-添加平均值辅助线-数据特殊标记-自定义label

效果如下:


代码奉上

import React from "react";
import {
  G2,
  Chart,
  Geom,
  Axis,
  Tooltip,
  Coord,
  Label,
  Legend,
  View,
  Guide,
  Shape,
  Facet,
  Util
} from "bizcharts";
import {
  g2Tooltip, getMinNumber, getMaxNumber
} from "./config";
import { formatW } from "../../util";
import './GroupedColumn.less'
import DataSet from "@antv/data-set";
const { Line } = Guide;
export default class GroupedColumn extends React.Component {
  getAvgNumber = (data = [], key) => {
    if (data.length > 0) {
      return data.reduce((pre, next) => pre + next[key], 0) / data.length;
    }
  }
  render() {
    const data = [
      {
        label: "Monday",
        series1: 12800,
        series2: 12260,
        url: 'http://img2.imgtn.bdimg.com/it/u=571610305,1553276443&fm=26&gp=0.jpg'
      },
      {
        label: "Tuesday",
        series1: 11800,
        series2: 11300,
        url: 'http://img2.imgtn.bdimg.com/it/u=571610305,1553276443&fm=26&gp=4.jpg'
      },
      {
        label: "Wednesday",
        series1: 14950,
        series2: 13900,
        url: 'http://img2.imgtn.bdimg.com/it/u=571610305,1553276443&fm=26&gp=3.jpg'

      },
      {
        label: "Thursday",
        series1: 14500,
        series2: 10390,
        url: 'http://img2.imgtn.bdimg.com/it/u=571610305,1553276443&fm=26&gp=2.jpg'

      },
      {
        label: "Friday",
        series1: 10170,
        series2: 10100,
        url: 'http://img2.imgtn.bdimg.com/it/u=571610305,1553276443&fm=26&gp=1.jpg'

      }
    ];
    const ds = new DataSet();
    const dv = ds.createView().source(data);
    dv.transform({
      type: "fold",
      fields: ["series1", "series2"],
      // 展开字段集
      key: "type",
      // key字段
      value: "value", // value字段
      callback: obj => {
        obj.value = [obj.series1, obj.series2];
        return obj;
      }
    });
    const scale = {
      type: { formatter: d => ({ series1: '点赞', series2: '评论' }[d]) },
      value: {
        min: getMinNumber('series1', data) > getMinNumber('series2', data) ? getMinNumber('series2', data) : getMinNumber('series1', data),
        max: getMaxNumber('series1', data) > getMaxNumber('series2', data) ? getMaxNumber('series1', data) : getMaxNumber('series2', data),
        formatter: val => {
          return formatW(val);
        }
      }

    };
    var imageMap = {
      Wednesday: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1564465427722&di=531da562d84c2f093a200c0c19d22b0d&imgtype=0&src=http%3A%2F%2Fpic.90sjimg.com%2Fdesign%2F01%2F35%2F21%2F37%2F5a3c542fb97ba.png',
    };
    //自定义label
    const labelConfig = {
      htmlTemplate(text, item, index) {
        //由于官方不支持返回原始数据,所以需要自己取值,⚠️⚠️这里的text必须是唯一值
        const dataItem = data.filter(one => one.label == text)[0]
        return ` <div style="position: relative;top:60px;text-align:center">
          <div>
             <img 
               width='60px'
               height='60px'
               src=${dataItem.url}/>
          </div>
          <div style='width:100px'>${text}</div>
          <div style='width:100px'>${dataItem.series1}-${dataItem.series2}</div>
       </div>
        `
      }
    }
    return (
      <div>
        <div className='legend-customize'>
          <div>
            <div className='block-legend fire'></div>
            <div>最火视频</div>
          </div>
          <div>
            <div className='block-legend blue'></div>
            <div>点赞</div>
          </div>
          <div>
            <div className='block-legend green'></div>
            <div>评论</div>
          </div>
        </div>
        <Chart height={500} data={dv} scale={scale}
          padding={[70, 100, 130]}
          forceFit>
          <Coord />
          <Axis name="label" label={labelConfig}
          />
          <Axis name="value" />
          <Tooltip g2-tooltip={g2Tooltip} />
          <Geom
            type="interval"
            position="label*value"
            color={'type'}
            adjust={[
              {
                type: "dodge",
                marginRatio: 1 / 32
              }
            ]}
          />
          <Geom
            type="point"
            position="label*value"
            size={40}
            shape={[
              ['label', 'type'],
              function (name, type) {
                if (type == 'series1') {
                  return ["image", imageMap[name]];
                }
                return ['image', null];
              }
            ]}
          />

          <Guide>
            <GuideLine content='近30条视频平均评论' middle={this.getAvgNumber(data, 'series1')} />
            <GuideLine content='近30条视频平均点赞' middle={this.getAvgNumber(data, 'series2')} color='#3AA1FF' />

          </Guide>
        </Chart>
      </div>
    );
  }
}


const GuideLine = ({ middle, color = '#2fc25b', content = '平均' }) => {
  return <Line
    top
    start={{ label: "Monday", value: middle }}
    end={{ label: "Friday", value: middle }}
    lineStyle={{
      stroke: color,
      lineDash: [0, 1, 1],
      lineWidth: 1,
    }}
    text={{
      position: 'end',
      style: {
        fill: color,
      },
      content: content,
    }}
  />

}
总结:在使用某个组件时,不要过分依赖组件。刚开始时我总是询问是否支持返回原始数据,觉得不支持就不好处理,以至于钻入了死胡同,这个时候建议广大开发者,要想想自己想要的结果,我们可以改变自己的解决方式,这样你就会一步一步接近自己的结果



2.关于自定义htmlContent

官网bug


官网代码

<Tooltip  useHtml htmlContent={(title, items) => {
      return `<div class="g2-tooltip">
                <div class="g2-tooltip-title">${title} </div>
                <ul><li>${JSON.stringify(items)}</li></ul>
                </div>`}}
  />

修改后自定义Tooltip效果图


⚠️必须手动写css,否则自定义的会样式会bug

//js中class
<Tooltip       
htmlContent={function (title, items) {
            const { list = [] } = items[0]             
 return `<div class='custom-tooltip'>你需要的内容<div>`
}
/>//css样式
.custom-tooltip {    
    position: absolute;   
    left: 20px;
    background: rgba(24, 24, 24, 0.9);
    color: #fff;
    width: 440px;
    height: auto;
    border-radius: 3px;
    font-size: 13px;
    right: 0;
    background-size: 100% auto;
    background-repeat: no-repeat;
    box-shadow: 0px 0px 10px #aeaeae;
    z-index: 999;
}


总结:在使用组件库的时候,不仅会使用到组件内部提供的东西,还需要了解其依赖的库,在G2中可以看到这个custom-tooltip的样式

3. 关于地图的使用

地图效果:


需要引入高德api

  <!-- 引入高德地图JSAPI -->  <script src="//webapi.amap.com/maps?v=1.3&key=89a0871d1c4bf418cd9ab88c01e3bd5d"></script>  <!-- 引入UI组件库(1.0版本) -->  <script src="//webapi.amap.com/ui/1.0/main.js"></script>

地图代码

import React, { Component } from 'react'const { AMap, AMapUI } = window;import {  Chart, Geom, Tooltip, Legend, Guide} from "bizcharts";import numeral from 'numeral'import DataSet from "@antv/data-set";const constructGeoJSON = (features) => {  if (!features) return false;  if (Array.isArray(features)) {    return {      type: 'FeatureCollection',      features: [...features],    };  }  return features;};/** 传入adcode获取geojson,部分数据需要处理一下*/const getGeojsonByCode = (adcode = 100000, withSub = true) => {  const { AMapUI } = window;  if (!AMapUI) {    return Promise.reject();  }  // 文档:https://lbs.amap.com/api/javascript-api/reference-amap-ui/geo/district-explorer  return new Promise((resolve, reject) => {    AMapUI.load("ui/geo/DistrictExplorer", DistrictExplorer => {      const districtExplorer = new DistrictExplorer();      districtExplorer.loadAreaNode(adcode, (error, areaNode) => {        if (error) {          reject();        }        let res = null;        if (withSub) {          res = areaNode.getSubFeatures();        } else {          res = areaNode.getParentFeature();        }        resolve(constructGeoJSON(res));      });    });  });};// 绘制思路// 前提:页面加载高德地图及其UI的SDK,参考html页面// 1、通过高德的数据接口获取geojson数据// 2、通过Dataset进行数据处理// 3、绘制class MapChart extends Component {  state = {    chinaGeo: null,  }  componentDidMount() {    getGeojsonByCode(100000, true).then(res => {      this.setState({        chinaGeo: res,      });    });  }  processGeoData = (geoData, dataValue) => {    const { features } = geoData    features.map((one) => {      const name = one.properties.name      dataValue.map((item) => {        if (name.includes(item.name)) {          one.value = item.value        }      })    })    const geoDv = new DataSet.View().source(geoData, {      type: 'GeoJSON',    });    return geoDv;  }  render() {    const  area =[      {        "key":"10105",        "name":"广东",        "value":0.1138      },      {        "key":"10125",        "name":"四川",        "value":0.0899      },      {        "key":"10102",        "name":"安徽",        "value":0.0695      },      {        "key":"10130",        "name":"浙江",        "value":0.0525      },      {        "key":"10112",        "name":"湖北",        "value":0.0505      },      {        "key":"10124",        "name":"上海",        "value":0.0495      },      {        "key":"10103",        "name":"福建",        "value":0.0484      },      {        "key":"10131",        "name":"重庆",        "value":0.0419      },      {        "key":"10115",        "name":"江苏",        "value":0.0402      },      {        "key":"10123",        "name":"陕西",        "value":0.0388      },      {        "key":"10121",        "name":"山东",        "value":0.0387      },      {        "key":"10109",        "name":"河北",        "value":0.0359      },      {        "key":"10116",        "name":"江西",        "value":0.0315      },      {        "key":"10113",        "name":"湖南",        "value":0.0304      },      {        "key":"10129",        "name":"云南",        "value":0.0294      },      {        "key":"10101",        "name":"北京",        "value":0.0246      },      {        "key":"10104",        "name":"甘肃",        "value":0.0232      },      {        "key":"10114",        "name":"吉林",        "value":0.0229      },      {        "key":"10107",        "name":"贵州",        "value":0.0223      },      {        "key":"10106",        "name":"广西",        "value":0.0220      },      {        "key":"10110",        "name":"河南",        "value":0.0190      },      {        "key":"10117",        "name":"辽宁",        "value":0.0152      },      {        "key":"10118",        "name":"内蒙古",        "value":0.0142      },      {        "key":"10128",        "name":"新疆",        "value":0.0142      },      {        "key":"10111",        "name":"黑龙江",        "value":0.0140      },      {        "key":"10126",        "name":"天津",        "value":0.0122      },      {        "key":"10122",        "name":"山西",        "value":0.0103      },      {        "key":"10108",        "name":"海南",        "value":0.0098      },      {        "key":"10119",        "name":"宁夏",        "value":0.0080      },      {        "key":"10120",        "name":"青海",        "value":0.0052      },      {        "key":"10127",        "name":"西藏",        "value":0.0020      }    ]    const { chinaGeo } = this.state;    if (!chinaGeo) {      return '数据加载中...'    }    const data = this.processGeoData(chinaGeo, area);    const scale = {      latitude: {        sync: true,        nice: false,      },      longitude: {        sync: true,        nice: false,      },      value: {        formatter: val => {          return numeral(val || 0).format('0.0%')        }      },    };    const { Image } = Guide;    return (      <div style={{ position: "relative" }}>        <Chart height={500} width={645} scale={scale} data={data} padding={[0, 0, 0, 90]}>          <Geom type="polygon" position="longitude*latitude"            style={{ lineWidth: 1, stroke: "white" }}            // color={['value', ['#31c5f8', '#61d3f8', '#89dcfd', '#b0e8f8', '#d8f3ff']]}            color={['value', ['#d9f4ff', '#33c5f6']]}            tooltip={['name*value', (name, value) => {              return {                name: name,                value: numeral(value || 0).format('0.0%')              }            }]}          >            <Tooltip showTitle={false} />            <Legend position='bottom-left'              offsetY={-130}              offsetX={-60}              slidable={false}              width={320}            />          </Geom>        </Chart>        <div style={{ position: "absolute", bottom: 100, right: 0 }}>          <img height='58' width='42'            src={require('../img/map-line.png')} />        </div>      </div>    );  }}export default MapChart

4.给图表后面加文字

效果图


   <Geom type="interval" position="name*value" >            //此处为每个柱图后字段            
            <Label content={["name*value", (name, value) => { 
             return numeral(value || 0).format('0.0%');         
               }]} />  
   </Geom>

多个返回一个
bizcharts.net/products/bi…


 <Geom
            type="intervalStack"
            position="State*人口数量"
            color={"年龄段"}
            >
           <Label content={["年龄段*人口数量", (name, value) => {
               console.log(name, value)
                if(name=='14至17岁')
                 return value;         
               }]} /> 
 </Geom>
总结:处理问题要举一反三,多想想你的目的,如果你写这个组件你会怎么处理这个问题呢?不想要就是过滤

5. 关于图例的文案修改成中文

官方图bizcharts.net/products/bi…


在cols添加如下代码:

user: { formatter: d => ({ a: '修改a', b: '修改b' }[d]) }

添加后效果图:


总结,关注每个细小的点,一个人修改一点,一堆人加起来是多大的工作量