React+G2 + G2plot 踩坑
参与图表组件库开发(要求使用Echarts或G2)
因为这两个都没用过,翻阅多篇文章,最终选择入坑G2,要用就用新技术!所以特此记录一些常用的,方便以后避坑。Echarts或G2对于我这样没用过的人来说API实在太多了没法记,所以我也不喜欢一点一点去看官方文档,更多是了解了基础原理直接看官网例子代码,不知道有人和我一样么?所以常用的我都会在下面代码注释中体现到底是干什么的。不会将某个属性展开说,什么包含那些等,遇到问题知道去找那个属性关键词就可以了
G2核心概念视觉通道
在 G2 中我们实现了如下类型的视觉通道(也称图形属性):、
- position(位置),二维坐标系内可以映射到 x,y,三维坐标系可以映射到 x,y,z。
- color(颜色),包含了色调、饱和度和亮度。
- size(大小),不同的图形对大小的定义有所差异。
- shape(形状),图形的形状决定了某个图表类型的表现方式。例如 点图,可以使用圆点、三角形、小 的图片表示;线图可以使用折线、曲线、点线等表现形式。
G2 图表组成
1.G2在react中的写法,看了各种写法,个人更加偏爱这种:
//写在useEffect当中我是觉得方便一些,但是有缺陷就是在外面找不到chart实例,没法对chart做操作
//所以有知道的大佬可以说一下一般标准的写法是怎么写的
useEffect(() => {const chart = new Chart({container: container,autoFit: true,height: 500,});...chart.interval().position('type*value')...chart.render();},[ ])
2.示例图加注释:
a.圆形柱状图?(颜色随数据改变)
我做的:
要求的:
//图片中渐变边框我选择用css实现,G2应该也可以实现但是很麻烦
//可以理解为两个极坐标的柱状图,一深一浅重叠
const used=68; //使用率 假数据
let colors=['#D3EAFF', '#2495FF']; //颜色假数据
const data1: object[] = []; //深色假数据for (let i = 0; i < 20; i++) { //控制柱状个数data1.push({type: i + '',value: 10,});}let data2: object[]= []; //浅色假数据for (let i = 0; i < 20; i++) {const item = { type: '', value: 1 };item.type = i + '';item.value = 10;let transused = used / 5; //100为满,20个柱子所以/5 转换if (i >= transused) {item.value = 0;}data2.push(item);}const chart = new Chart({ //创建Chart实例container: container, //容器id名称autoFit: true, //是否随着屏幕自适应宽高height: 500, //也可以单独设置宽高});chart.legend(false); //不显示图例chart.tooltip(false); //不显示提示 就是鼠标滑过时的文字const view1 = chart.createView(); //创建view实例,View 是图层容器的概念,和ps中图层差不多,重叠顺序,没有重叠的图直接用chart就行view1.data(data1); //绑定数据源view1.axis(false); //不显示坐标轴view1.coordinate('polar', { //坐标类型:极坐标innerRadius: 0.7, //极坐标内半径radius: 100, //极坐标半径 innerRadius,radius,以及假数据的20个数,可以调整其比例达到自己想要的间隙或者柱子宽度});view1.interval() //Geom几何标记类型,interval:使用矩形或者弧形,用面积来表示大小关系的图形,一般构成柱状图、饼图等图表。//下面都是图形属性.position('type*value') //坐标系映射.color(String(used), (cValue) => { //颜色,支持字符串和这种写法,不接受变量,我试过了if (Number(cValue) >= 90) {return '#FBDBD9';} else if (Number(cValue) >= 70) {return '#FEF1DD';}return colors[0];})// .color('value',String(colors[0])).size(6); //大小const view2 = chart.createView(); //图层二 同上view2.data(data2);view2.axis(false);view2.coordinate('polar', {innerRadius: 0.7,radius: 100,});view2.interval().position('type*value')// .color(String(colors[1])).color(String(used), (cValue) => {if (Number(cValue) >= 90) {return '#EF5C52';} else if (Number(cValue) >= 70) {return '#F8B755';}return colors[1];}).size(6);chart.render(); //渲染}, []);
b.柱状图(y轴分割线虚线,重叠,隔行显示文字)
const data = [{ type: 'this', info: '人脸搜索', value: 760 },{ type: 'this', info: '人体搜索', value: 520 },{ type: 'this', info: '机动车搜索', value: 820 },{ type: 'this', info: '非机动车搜索', value: 300 },];let max = 0; //用来计算y坐标轴最高数值data.forEach((item) => {if (item.value > max) {max = item.value;}});let data1 = data.map((item) => { //阴影灰色柱状图return { type: 'all', info: item.info, value: Math.ceil(max / 600) * 600 - item.value };});data.unshift(...data1);const chart = new Chart({container: 'container',autoFit: true,});chart.data(data); //导入数据chart.interval().position('info*value').size(12).label('value', (xValue) => { //显示文本内容return {content: xValue + '次',};}).color('type', ['#EEEEEE', '#2495FF'])//重叠.adjust('stack'); //设置数据调整方式,stack:层叠,第一个那个图表应该也可以用这个实现,稍微简单一点chart.axis('value', { //坐标轴title: null, // 不展示 xField 对应坐标轴的标题label: { //隔行展示坐标轴文本formatter: (val) => { // 使用 formatter 回调函数if ((Number(val) / 300) % 2 !== 0) {return '';} else {return val;}},},//网格虚线grid: {line: {style: {stroke: '#d9d9d9',lineWidth: 1,lineDash: [2, 2],},},},});chart.scale('value', { //度量(Scale)用于定义数据的类型和展示方式nice: true, //自动调整 min、max 。tickInterval: 300, //控制间隔});//所有提示都不展示chart.tooltip(false);//所有图例都不展示chart.legend(false);//交互:鼠标在画布上移动是,对应的区域出现背景框chart.interaction('active-region');chart.render();
c.自定义图形柱状图(渐变,隔行换色,最大的感受G2文档太少了,不详细)
按照下面写法没法实现,我也不知道为什么,而且无法自适应了,奈何文档资料有限,百度都百度不到,没办法耗了半天天打算用Echarts重写…,但是G2写法也亮出来,请大家看看问题在哪里
G2:
import { Chart,registerShape } from '@antv/g2';
registerShape('interval', 'triangle', { //注册自定义shape接口 getPoints(cfg:any) { // 定义关键点const x = cfg.x;const y = cfg.y;const y0 = cfg.y0;const width = cfg.size;return [{ x: x - width / 2, y: y0 },{ x: x, y: y },{ x: x + width / 2, y: y0 },];},// 2. 绘制draw(cfg, group) {const points = this.parsePoints(cfg.points); // 将0-1空间的坐标转换为画布坐标//添加自定义shape ,实测其中image,dom类型,没法用,查不到为什么,官网也没有,那位大佬可以说一下为什么么?具体地址:https://g.antv.vision/zh/docs/api/canvasgroup.addShape('path', { //采用路径方式attrs: {//这个路径就是< svg >< path d="............" >< /svg > d中的值path:'M2.08269 86.5C28.1667 72.8126 45.3971 46.8239 48 17.6787C50.6029 46.8239 67.8333 72.8126 93.9173 86.5H2.08269Z',stroke:cfg.color, //线条颜色lineWidth:2, //线条宽度fill:cfg.color, //填充颜色},});return group;},});const data = [{ type: 'this', info: '功能名称1', value: 1360 },{ type: 'this', info: '功能名称2', value: 1000 },{ type: 'this', info: '功能名称3', value: 980 },{ type: 'this', info: '功能名称4', value: 900 },{ type: 'this', info: '功能名称5', value: 860 },];let max = 0;data.forEach((item) => {if (item.value > max) {max = item.value;}});const chart = new Chart({container: 'container',autoFit: true,});chart.data(data);chart.interval().position('info*value').label('value', (xValue) => {return {content: xValue + '次',};}).size(100).color('info',(cVal)=>{let count=0;data.forEach((item,index)=>{if(item.info===cVal){count=index+1}});//隔行变色if(count%2!==0){//渐变写法return 'l(90) 0:rgba(36, 149, 255, 0.7) 1:rgba(36, 149, 255, 0.1)'}else{return 'l(90) 0:rgba(75, 215, 157, 0.7) 1:rgba(75, 215, 157, 0.1)'}}).shape('triangle');//坐标轴chart.axis('value', {// 不展示 xField 对应坐标轴的标题title: null,//网格虚线grid: {line: {style: {stroke: '#d9d9d9',lineWidth: 1,lineDash: [2, 2],},},},});//控制间隔chart.scale('value', {nice: true,tickInterval: 500,});//所有提示都不展示chart.tooltip(false);//所有图例都不展示chart.legend(false);//交互:鼠标在画布上移动是,对应的区域出现背景框chart.interaction('active-region');chart.render();
Echarts:
import EchartsReact from 'echarts-for-react';
const Chart5e1 =()=>{type data={name:string,value:number}[]let data:data = [{name:'指挥中心',value:770},{name:'科信',value:450},{name:'刑侦',value:300},{name:'XXX',value:255},{name:'情报',value:203},
]
let xAxisData:any=[];
let seriesData:any=[];
let max=0;
//因为边框也需要隔行变色,但是borderColor属性不支持函数方式,所以只能加到data中data.forEach((item: any, index: number) => {if (item.value > max) {max = item.value;}if (index % 2 !== 0) {seriesData.push({value: item.value,itemStyle: {borderColor: 'rgba(75, 215, 157, 0.7)',},});} else {seriesData.push({value: item.value,itemStyle: {borderColor: 'rgba(36, 149, 255, 0.7)',},});}xAxisData.push(item.name);});
const option = {tooltip : {trigger: 'axis',axisPointer : { // 坐标轴指示器,坐标轴触发有效type : 'none' // 默认为直线,可选为:'line' | 'shadow'},backgroundColor:'#405685',textStyle : {color: '#E5EDF5',decoration: 'none',fontSize: 12,},formatter:function(param){let tip = '';tip = param[0].name+': '+ param[0].value;return tip}},grid: { //图表与外层div间距,可以理解为paddingtop: '8%',right: '10%',left: '15%',bottom: '15%'},xAxis: { type: 'category',data: xAxisData,axisLine: {lineStyle: {color: '#C3C8CC'}},axisTick: {show: false},axisLabel: {color: '#000',textStyle: {fontSize: 12},formatter:function(value){var ret = "";//拼接加\n返回的类目项 var maxLength = 4;//每项显示文字个数 var valLength = value.length;//X轴类目项的文字个数 var rowN = Math.ceil(valLength / maxLength); //类目项需要换行的行数 if (rowN > 1)//如果类目项的文字大于5,{ var temp = "";//每次截取的字符串 var start = 0;//开始截取的位置 var end = maxLength;//结束截取的位置 temp = value.substring(start, end)+'\n'+value.substring(end, valLength) ret += temp; //凭借最终的字符串 return ret; }else{return value; }},}, splitLine: {show: false,}},yAxis: {type: 'value',axisLabel: {formatter: function (value: any) {return parseInt(value);},color: '#000',},max:Math.ceil(max/500)*500,interval:500,axisLine:{show:false},axisTick: {show: false},splitLine: {lineStyle: {type:'dashed',// 使用深浅的间隔色color: '#273858'}},},series: [{data: seriesData,type: 'pictorialBar',barCategoryGap: '-20%',symbol: 'path://M2.08269 86.5C28.1667 72.8126 45.3971 46.8239 48 17.6787C50.6029 46.8239 67.8333 72.8126 93.9173 86.5H2.08269Z',itemStyle: {normal: {//图形的间隔变色,渐变color:function(params){if(params.dataIndex % 2 == 0){return {type: 'linear',x: 0,y: 0,x2: 0,y2: 1,colorStops: [{offset: 0, color: 'rgba(36, 149, 255, 0.7)' // 0% 处的颜色}, {offset: 1, color: 'rgba(36, 149, 255, 0.1)' // 100% 处的颜色}],globalCoord: false // 缺省为 false}}else{return {type: 'linear',x: 0,y: 0,x2: 0,y2: 1,colorStops: [{offset: 0, color: 'rgba(75, 215, 157, 0.7)' // 0% 处的颜色}, {offset: 1, color: 'rgba(75, 215, 157, 0.1)' // 100% 处的颜色}],globalCoord: false // 缺省为 false}}}// color:{// type: 'linear',// x: 0,// y: 0,// x2: 0,// y2: 1,// colorStops: [{// offset: 0, color: 'rgba(36, 149, 255, 0.7)' // 0% 处的颜色// }, {// offset: 1, color: 'rgba(36, 149, 255, 0.1)' // 100% 处的颜色// }],// globalCoord: false // 缺省为 false// }},},label: {normal: {show: true,position:'top',color:'#000',fontSize:14,formatter: function(params){var str = '';str = params.value;return str},fontFamily: 'DINPro'},},z: 10}, {name: 'glyph',type: 'pictorialBar',barGap: '-100%',symbolPosition: 'end',symbolSize: 50,symbolOffset: [0, '-120%'],}]
};return <EchartsReact option={option} style={{ width: '100%', height: '100%' }} />;
}
export default Chart5e1;
效果:
d.水波图组件(G2plot实现,个人感觉和G2没有太大区别)
组件:
import { useEffect, useRef, useState } from 'react';
import { Liquid } from '@antv/g2plot';//唯一id,颜色,数值,文本,大小
const LiquidChart=({ids,color,percent,textinfo,size})=>{const [liquidPlot,setLiquidPlot]=useState(null)useEffect(()=>{//数值更新销毁重绘if(liquidPlot!==null){liquidPlot.destroy()}const liquidPlot1 = new Liquid('sbchart'+ids, { //新建percent: percent, //百分比width:size, height:size,liquidStyle:{ //水波样式fill:color,stroke:color},statistic :{ //文本配置title:{formatter:(v)=>{return v.percent*100+'%'},style:{fontSize:size/9,color: '#093049'}},content:{content:textinfo,style:{fontSize:size/15,color: '#093049'}}}});liquidPlot1.render(); setLiquidPlot(liquidPlot1)},[color,percent,textinfo,size])return <div id={'sbchart'+ids}></div>
}
export default LiquidChart
使用
<LiquidChartids={1}color={"#A595FE"}percent={cpu}textinfo={"CPU使用率"}size={220}>
</LiquidChart>
e.曲线面积图表组件(G2plot实现)
组件
import { useEffect, useRef, useState } from 'react';
import { Line } from '@antv/g2plot';
const LineChart=({data,height,ids,legend=true,width,xAxis=true,yAxis=true})=>{const [linePlot1,setLinePlot1]=useState(null)useEffect(()=>{ //数值更新销毁重绘if(linePlot1!==null){linePlot1.destroy()}const linePlot = new Line('box'+ids, {data,padding:'auto',xField: 'date', //x轴映射yField: 'value', //y轴映射height:height, seriesField: 'name', //拆分字段,在分组条形图下同 groupField、colorField,在堆积条形图下同 stackField、colorField。width:width,yAxis: yAxis?{label: {formatter: (v) => `${v}`,},grid:{ line:{style:{lineDash:[4,5] //设置轴线为虚线}}}}:false,xAxis: xAxis? {range: [0, 1], //从x轴起点开始画,画到最后,不会存在与y轴存在间距label: {formatter: (v) => {return v},},}:false,legend: legend?{position: 'top-left', //设置图例位置}:false,tooltip: {formatter: (datum) => {return { name: datum.name, value: datum.value + 'Mbps' }; //设置提示},},smooth: true,// 配置折线趋势填充area: {style: ({ name }) => {const { colors10 } = linePlot.chart.getTheme();return {fill:name === '接入宽带'? `l(90) 0.3:${colors10[0]} 1:rgba(255,255,255,0.2)`: `l(90) 0.3:${colors10[1]} 1:rgba(255,255,255,0.2)`,};},},animation: {appear: {animation: 'wave-in',duration: 3000,},},});linePlot.render();setLinePlot1(linePlot)},[data])return <div id={"box"+ids}></div>
}
export default LineChart
使用
const dataarr=[
{name: "输入宽带",date:'1s',value: 82},{name: "输出宽带",date:'1s',value: 150},{name: "输入宽带",date:'2s',value: 96},{name: "输出宽带",date:'2s',value:126},
]
<LineChartdata={dataarr}height={240}ids={1}></LineChart>
f.双向柱状图(G2plot实现)
const [max,setMax]=useState(0)
const devicedata = [
{ type: `单通道设备视频通道`, sales: 150,aname:1 },
{ type: `单通道设备视频通道`, sales: -15,aname:0 },
{ type: `多通道设备视频通道`, sales: 120,aname:2 },
{ type: `多通道设备视频通道`, sales: -120,aname:0 }
]
let max=0
devicedata.forEach(item=>{if(Math.abs(item.sales)>max){max=Math.abs(item.sales)}})
setMax(max)
useEffect(()=>{if(bar1!==null){bar1.destroy()}const bar = new Bar('container', {data:devicedata,xField: 'sales',yField: 'type',meta: { // sales映射的最大最小值sales: {max: max, min: -max, //防止出现数据只有正值,导致负值的半轴不显示}},legend: {position: 'top-left',},width:500,yAxis:{label:{formatter: (v) => {if(v==="多通道设备视频通道"){ //y轴文本换行return `多通道设备视频通道`}return `单通道设备视频通道`},},},xAxis:{grid:{line:{style:{lineDash:[4,5] //x轴线虚线}}},barWidthRatio:0.2, //柱子宽度seriesField: 'aname', // 部分图表使用 seriesField //设置正值不同类颜色不同,负值都为灰色color: ({ aname }) => {if(aname === 0){return '#8E9DAF';}else if(aname===1){return '#4DAAFF';}return '#13C2C2';},tooltip:{formatter: (datum) => {return { name: datum.aname!==0?'在线':'离线', value: Math.abs(datum.sales) };},title:(v)=>{return v.trim()+'(个)'}},legend:false,barBackground: {style: {fill: 'rgba(0,0,0,0.1)',borderRadius:50},},interactions: [{ type: 'active-region', enable: false }],});bar.render();setbar1(bar)},[devicedata,max])
**
g.曲线图(自定义面积渐变,自定义图例,自定义x绘画区域,边框)
const AreasplineChart = ({id,className}:{id:string,className?:string}) => {const data = [{"year": "1","value": 4055,"category": "人脸"},{"year": "1","value": 1756,"category": "人体"},{"year": "1","value": 5664,"category": "机动车"},{"year": "1","value": 6004,"category": "非机动车"},{"year": "2","value": 2106,"category": "人脸"},{"year": "2","value": 1783,"category": "人体"},{"year": "2","value": 5019,"category": "机动车"},{"year": "2","value": 1005,"category": "非机动车"},.... ........]const fillcolor = [{type:'人脸',color:'l(90) 0.1:rgba(77, 170, 255, 0.3) 1:rgba(255,255,255,0.1)',},{type:'人体',color:'l(90) 0.1:rgba(165, 149, 254, 0.3) 1:rgba(255,255,255,0.1)',},{type:'机动车',color:'l(90) 0.1:rgba(32, 203, 206, 0.3) 1:rgba(255,255,255,0.1)',},{type:'非机动车',color:'l(90) 0.1:rgba(255, 170, 91, 0.3) 1:rgba(255,255,255,0.1)'}]useEffect(()=>{const line = new Line('container'+id, {data,xField: 'year',yField: 'value',seriesField: 'category',appendPadding:[24,0,0,0],height:220,xAxis:{//去除x轴的刻度线tickLine:null//定义画图区域,默认从第一个刻度开始,这样设置会从0开始画//range: [0, 1],},yAxis: {label: {// 数值格式化为千分位formatter: (v) => `${v}`.replace(/\d{1,3}(?=(\d{3})+$)/g, (s) => `${s},`),},//y轴值的间隔tickInterval:2000,grid:{ line:{style:{lineDash:[4,5] //设置轴线为虚线}}}},legend: {position: 'top-left',itemWidth:80,//自定义图例的图标形状marker:{//内置类型symbol:'square',style:(value)=>{return {fill: value.stroke,lineJoin: "round",lineWidth: 6,r: 2,stroke: value.stroke,}}}},//是否为圆滑曲线,false为折线图smooth: true,//设置线颜色color:['rgba(77, 170, 255, 1)','rgba(165, 149, 254, 1)','rgba(32, 203, 206, 1)','rgba(255, 170, 91, 1)'],// 配置折线趋势填充area: {style: ({category}) => {return {fill: fillcolor.filter(v=>v.type===category)[0].color};},},});line.render();},[])return (<div id={`container${id}`} className={className}></div>)
}
h.折线图(自定义背景,自定义节点样式)
const StockPendingChart = ({id}:{id:string}) => {let data:object[]=[]for(let i=0;i<24;i++){data.push({"year": `${i<10?'0'+i : i}:00`,"value": Math.random()*10000,"category": "人脸"});data.push({"year": `${i<10?'0'+i : i}:00`,"value": Math.random()*10000,"category": "人体"});data.push({"year": `${i<10?'0'+i : i}:00`,"value": Math.random()*10000,"category": "机动车"});data.push({"year": `${i<10?'0'+i : i}:00`,"value": Math.random()*10000,"category": "非机动车"});}const [typechart,setTypechart]=useState(null)useEffect(()=>{const line = new Line('container'+id, {data,xField: 'year',yField: 'value',seriesField: 'category',height:200,//可以理解为图表与图例之间那些的间距appendPadding:[24,0,0,0],xAxis: {nice: true,label: {formatter: (name) => name,},// 坐标轴线的配置项 null 表示不展示tickLine:null,grid: {line: {style: {//设置x轴的那个灰色背景lineWidth:42,stroke: 'rgba(239, 242, 244, 0.5)',},},},},yAxis: {label: {// 数值格式化为千分位formatter: (v) => `${v}`.replace(/\d{1,3}(?=(\d{3})+$)/g, (s) => `${s},`),},tickInterval:2000,grid:{ line:{style:{lineDash:[4,5] //设置轴线为虚线}}}},legend: {position: 'top-left',marker:{symbol:'square',style:(value)=>{return {fill: value.stroke,lineJoin: "round",lineWidth: 6,r: 2,stroke: value.stroke,}}}},//配置折线图每个节点的样式point: {size: 5,style: {lineWidth: 1,fillOpacity: 1,},shape: 'circle'},// 配置折线线条颜色color:['rgba(77, 170, 255, 1)','rgba(165, 149, 254, 1)','rgba(32, 203, 206, 1)','rgba(255, 170, 91, 1)'],});line.render();},[])return (<div id={`container${id}`} style={{flex:'1'}}></div>)
}
I.环图(自定义图例样式,G2plot ,G2分别实现)
//G2plot 实现
import React, { useEffect, useState } from "react";
import { Pie } from "@antv/g2plot";
import { TransNumber } from "@src/pages/utils";export interface chartdata {type: string;value: number;
}const PieChart = ({ id, datasouce }: { id: string, datasouce: chartdata[] }) => {const [typechart, setTypechart] = useState<any>(null)useEffect(() => {if (typechart) {typechart.destroy()}const piePlot = new Pie('container'+id, {data:datasouce,height:260,legend:{//图例项水平布局layout: 'horizontal',position: 'right',offsetX:-20,//是否分页flipPage:false,//图例项水平间距itemSpacing:24,//图例项垂直间距itemMarginBottom:34,//图例与图形的占比maxWidthRatio:0.6,//图例图标marker:{symbol:'square'},itemWidth:120,//图例名itemName:{style:{fontSize:14}},//图例值itemValue:{//水平居右alignRight:true,formatter(text) {return TransNumber(datasouce?.filter(item=>item.type===text)[0].value)},style:{fontSize:14}}},//空白间距pieStyle:{lineWidth:6},angleField: 'value',colorField: 'type',radius: 0.6,innerRadius: 0.6,label: false,interactions: [{ type: 'element-selected' }, { type: 'element-active' }],statistic: undefined,});piePlot.render();setTypechart(piePlot)}, [datasouce])return (<div id={`container${id}`}></div>)
}export default PieChart
//G2 实现
const ChildrenTypeChart = () => {const [typechart,setTypechart]=useState(null)useEffect(()=>{if(typechart!==null){typechart.destroy()}const data = [{ type: '消费连接异常', value: 15 },{ type: '任务启动异常', value: 15 },{ type: '网络连接异常', value: 10 },{ type: '数据库异常', value: 10 },{ type: '服务自身异常', value: 10 },{ type: '其他', value: 10 },];// 可以通过调整这个数值控制分割空白处的间距,0-1 之间的数值const sliceNumber = 0.01;// 自定义 other 的图形,增加两条线registerShape('interval', 'slice-shape', {draw(cfg, container) {const points= cfg.points;let path = [];path.push(['M', points[0].x, points[0].y]);path.push(['L', points[1].x, points[1].y - sliceNumber]);path.push(['L', points[2].x, points[2].y - sliceNumber]);path.push(['L', points[3].x, points[3].y]);path.push('Z');path = this.parsePath(path);return container.addShape('path', {attrs: {fill: cfg.color,path,},});},});const chart = new Chart({container: 'container',autoFit: true,height: 300,width:270});chart.legend({flipPage:false,itemWidth:120});chart.data(data);chart.coordinate('theta', {radius: 0.55,innerRadius: 0.65,});chart.tooltip({showTitle: false,showMarkers: false,});chart.interval().adjust('stack').position('value').color('type').shape('slice-shape');chart.render();setTypechart(chart)},[])return (<div id="container"></div>)
}
J.柱状图(自定义背景)
import React, { useEffect, useState } from "react";
import { Column } from '@antv/g2plot';
export interface chartdata {name: string;value: number;type: string;
}
const ClounmChart = ({ id, datasouce }: { id: string, datasouce: chartdata[] }) => {const [typechart, setTypechart] = useState<any>(null)useEffect(() => {if (typechart) {typechart.destroy()}const stackedColumnPlot = new Column('container' + id, {data: datasouce,isStack: true, //是否累叠xField: 'name',height: 240,appendPadding:[20,0,0,0],yField: 'value',legend: {position: 'top-right',},xAxis: {nice: true,// 坐标轴线的配置项 null 表示不展示tickLine: null,grid: {line: {style: {lineWidth: 36, //背景宽度lineDash: [4, 4], //背景虚线,不要这个就是第二张图stroke: 'rgba(238, 239, 243, 1)', //背景颜色},},},},yAxis: {label: {// 数值格式化为千分位formatter: (v) => `${v}`.replace(/\d{1,3}(?=(\d{3})+$)/g, (s) => `${s},`),},// tickInterval:2000,grid: {line: {style: {lineDash: [4, 5] //设置轴线为虚线}}}},seriesField: 'type',columnWidthRatio: 0.2,});stackedColumnPlot.render();setTypechart(stackedColumnPlot)}, [datasouce])return (<div id={`container${id}`}></div>)
}export default ClounmChart
K.分组柱状图(自定义形状样式,间距等)
import { useEffect, useState } from "react";
import { Column } from '@antv/g2plot';
const ColumnGroupChart = ({ id, currentDetail }: { id: string, currentDetail: any }) => {const [currentChart, setCurrentChart] = useState<any>(null)const types = ['数据消费数量', '数据转换成功数量', '数据转换失败数量 ', '数据入库成功数量', '数据入库失败数量']useEffect(() => {let data: any[] = []currentDetail?.detailData?.forEach((item: any) => {data.push({type: types[item.type-1],datatype: "人脸",value: item?.faceCount})data.push({type: types[item.type-1],datatype: "人体",value: item?.bodyCount})data.push({type: types[item.type-1],datatype: "机动车",value: item?.faceCount})data.push({type: types[item.type-1],datatype: "非机动车",value: item?.nomotorVehicleCount})});if (currentChart) {currentChart.destroy()}const column = new Column('container' + id, {data: data,xField: 'type',yField: 'value',height: 400,seriesField: 'datatype',isGroup: true,appendPadding: [24, 0, 0, 0],// intervalPadding:160,maxColumnWidth: 16,xAxis: {tickLine: null,},yAxis: {label: {// 数值格式化为千分位formatter: (v) => `${v}`.replace(/\d{1,3}(?=(\d{3})+$)/g, (s) => `${s},`),},grid: {line: {style: {lineDash: [4, 5] //设置轴线为虚线}}}},legend: {position: 'top-left',marker: {symbol: 'square',style: (value) => {return {fill: value.fill,lineJoin: "round",lineWidth: 6,r: 2,stroke: value.fill,}}}},columnStyle: {radius: [20, 20, 20, 20],},columnBackground: {style: {fill: 'rgba(245, 245, 245, 1)'}},color: ['rgba(77, 170, 255, 1)', 'rgba(165, 149, 254, 1)', 'rgba(32, 203, 206, 1)', 'rgba(255, 170, 91, 1)'],});column.render();setCurrentChart(column)// eslint-disable-next-line react-hooks/exhaustive-deps}, [])return <div id={`container${id}`} style={{ marginTop: '40px' }}></div>
}
export default ColumnGroupChart
L.区间柱状图
import { Column } from '@antv/g2plot';const data = [{ type: '分类一', type1:'lon',values: [76, 100] },{ type: '分类一', type1:'bon',values: [0, 72] },{ type: '分类1', type1:'lon',values: [88, 168] },{ type: '分类1', type1:'bon',values: [0, 84] },{ type: '分类3', type1:'lon',values: [42, 196] },{ type: '分类3', type1:'bon',values: [0, 38] },{ type: '分类4', type1:'lon',values: [99, 111] },{ type: '分类4', type1:'bon',values: [0, 95] },{ type: '分类5', type1:'lon',values: [62, 160] },{ type: '分类5', type1:'bon',values: [0, 58] },
];const barPlot = new Column('container', {data,xField: 'type',yField: 'values',isRange: true,maxColumnWidth:32,seriesField: 'type1',
});barPlot.render();
React+G2 + G2plot 踩坑相关推荐
- native react ssh_React Native踩坑笔记(持续更新中...)
最近发现市场上对React Native开发人员的需求挺多的,自己就想学习一下React Native,多一门技术,好将来买个好价位.嘿嘿! 在学习React Native中碰到了不少坑,再次记录下来 ...
- React Native 入门踩坑
开发环境搭建及环境变量配置 开发rn第一步需要配置安卓环境 android studio下载地址: https://developer.android.google.cn/studio/archive ...
- React项目部署踩坑
1. 文件路径问题,在 package.json中增加homepage 2.css中图片路径问题,建议直接用import的引入使用 import bgimg from './images/bg.jpg ...
- truffle unbox react 初始化踩坑
truffle unbox react 初始化踩坑 1.文件位置引起的错误 一定要在package.json 所在文件夹下npm run start ,新版本一定要进入client文件夹再npm ru ...
- React Native之React Navigation踩坑
自动重装系统之后,已经很长一段时间没有来写React Native了,今天空闲之余,决定重新配置React Native的开发环境,继续踩坑... React Native的开发环境配置狠简单,只要依 ...
- 丰田项目踩坑手记(REACT)
丰田项目踩坑手记(REACT) 路由配置的时候:要引入hash路由不是browswer那个 路由参考网址:react-guide.github.io/react-route- 路由网址:reacttr ...
- python array赋值_从踩坑学Python内部原理(5):执行时机的差异
(给Python开发者加星标,提升Python技能) 英文:Satwik Kansal,翻译:暮晨 Python开发者整理自 GitHub [导读]:Python 是一个设计优美的解释型高级语言,它提 ...
- eslint 无法格式化ts_vscode-eslint的踩坑实践--typescript无法格式化
Apple iPhone 11 (A2223) 128GB 黑色 移动联通电信4G手机 双卡双待 4999元包邮 去购买 > vscode-eslint的踩坑实践--typescript无法格式 ...
- Next.js踩坑入门系列(七) —— 其他相关知识
Next.js踩坑入门系列 (一) Hello Next.js (二) 添加Antd && CSS (三) 目录重构&&再谈路由 (四) Next.js中期填坑 (五) ...
最新文章
- docker-3-常用命令(上)
- Python学习笔记: Python 标准库概览二
- oracle 去除重复的信息
- icoding复习1,2
- springboot+thymeleaf+pageHelper带条件分页查询
- 团队任务2:冲刺前的准备
- 【转载】JSON介绍
- RocketMq : 消费消息的两种方式 pull 和 push
- Spring Boot + Security + Thymeleaf + Activiti 快速开发平台项目
- C# .NET 使用DotNetZip开源类库 处理 压缩/解压 Zip 处理乱码情况
- .NET之我见系列 - 类型系统(上)
- Security+ 学习笔记51 风险分析
- Xcode常用的快捷键有哪些?
- 拓端tecdat|R语言 RevoScaleR的大规模数据集决策树模型应用案例
- 2021-08-06MATLAB深度学习简单应用
- Linux redis常用命令
- 2016.09.20回顾
- ImportError: cannot import name ‘_validate_lengths‘解决方法
- linux mint 使用软件管理器安装软件
- 数据结构软件测试,资讯详情-java常见数据结构-柠檬班-自动化测试-软件测试培训-自学官网...