一、所谓前言
公司某个项目后期优化,分组区间柱状图改成分组箱形图,因为这两天刚好用到箱形图,就想着分组箱形图与箱型图之间应该和柱状图与分组柱状图一样,想着,就去查找官网,结果,就4个横坐标、3个图例,源数据居然有150条,3(图例)、4(横坐标)、5(单个箱形图数据)怎么也凑不到150,好吧,瞬间放弃了,换个思路。
输出了官网转换后的数据,12个,这才符合我的认知嘛。可是每个却又有4个字段,其中“value是什么意思?不得而知了。琢磨半天,也没想明白,于是在群里问,边问边自己不断研究,试着自己将value字段去掉,发现并没有什么影响,好吧……是我想多了,正常思路就行,谁让我一开始就怂了。
二、代码
import React from 'react';
import { Chart, Geom, Axis, Tooltip, Legend, G2 } from 'bizcharts';
interface IGropuedBoxChartProps {
height?: number;
data: any[];
xAxis: string; // x轴坐标
// yAxis: string; // y轴坐标
boxValue: string; // 代表值的字段
xNickName?: string;
yNickName?: string;
xUnitName?: string;
yUnitName?: string;
legendName: string; // 图例名称
xUnitPosition?: string[];
yUnitPosition?: string[];
colors?: string[];
}
/**
* 分组箱形图
* @param props
* @constructor
*/
const GropuedBoxChart: React.FC<IGropuedBoxChartProps> = props => {
const {
height = 400,
data = [],
xAxis,
xNickName,
yNickName,
legendName = [],
boxValue = '',
colors,
} = props;
const cols: {
[key: string]: { [key: string]: string | number | string[] | number[] | any };
} = {};
cols[xAxis] = {
alias: xNickName || '',
};
cols[boxValue] = {
alias: yNickName || '',
};
const toolTipItemTpl =
'<li data-index={index} style="margin-bottom:4px;">' +
'<span style="color:#242424;font-size:13px;">{name}</span><br />' +
'<span style="color:##999999;font-size:12px;">最高分:{high}分</span>, ' +
'<span>上四分位分:{q3}分</span>, ' +
'<span>平均分:{median}分</span>, ' +
'<span>下四分位分:{q1}分</span>, ' +
'<span>最低分:{low}分</span>' +
'</li>';
function getCurrentColor(key: string): string {
// 获取所有的图例信息
const lend: string[] = [];
data.forEach((item: any) => {
lend.push(item[`${legendName}`]);
});
const lendData = [...new Set(lend)];
const resultMap = [];
let result: string = '';
if (colors && colors.length > 0) {
// 传入颜色
for (let i = 0; i < colors.length && i < lendData.length; i += 1) {
const obj: any = {};
obj[`${lendData[i]}`] = colors[i];
resultMap.push(obj);
}
} else {
// 未传入颜色
for (let i = 0; i < lendData.length; i += 1) {
const obj = {};
obj[`${lendData[i]}`] = G2.Global.colors[i];
resultMap.push(obj);
}
}
if (resultMap && resultMap.length > 0) {
const rr = resultMap.filter((item: any) => item[`${key}`]);
if (rr && rr.length > 0) {
result = rr[0][`${key}`];
}
}
return result;
}
return (
<Chart height={height} data={data} padding="auto" forceFit scale={cols}>
<Axis
/>
<Tooltip
showTitle={false}
crosshairs={{
type: 'rect',
style: {
fill: '#E4E8F1',
fillOpacity: 0.43,
},
}}
itemTpl={toolTipItemTpl}
/>
<Legend
position="top-right"
textStyle={{
textAlign: 'start', // 文本对齐方向,可取值为: start middle end
fill: '#FFF', // 文本的颜色
}}
/>
<Geom
type="schema"
position={`${xAxis}*${boxValue}`}
shape="box"
color={[`${legendName}`, val => getCurrentColor(val)]}
style={[
`${legendName}`,
{
stroke: 'rgba(0, 0, 0, 0.45)',
fill: (val: any) => getCurrentColor(val),
fillOpacity: 0.3,
},
]}
adjust="dodge"
tooltip={
[
`${xAxis}*${legendName}*${boxValue}`,
(x: string, legend: string, values: string[]) => ({
name: `${legend}-${x}`,
low: values[0],
q1: values[1],
median: values[2],
q3: values[3],
high: values[4],
}),
] as any
}
/>
</Chart>
);
};
export default GropuedBoxChart;
三、小结
分组箱形图的难点在于数据结构,于我而言,主要是被官网的源数据吓到了,还有就是单个箱型图掌握的不好。
箱型图与其他图标我觉得最大的不同在于color需要明确的设置出来,单个箱形图好办,写死就行,而多个就比较麻烦了,需要根据数据自己处理下,而这里又涉及到颜色使用自带颜色还是自定义颜色的问题,代码中均有考虑到。
箱型图的tooltip也需要自定义,同样的,单个的好处理,官网demo有,而多个的情况下,官网demo的tooltip给人的感觉就有些敷衍了。按照单个的思路,写出了多个分组箱形图的tooltip,发现如果超过三四个,就会太长,最终索性,一个箱型图占一行,勉强能看得过去了。
很多事情在自己认输的那一刻就彻底输了,后面再怎么让努力都是垂死挣扎了。消极的心理暗示真的是害死人。做人也是,敲代码更是如此。