使用某图统计某学校最近几年人数变化情况,横坐标年份,纵坐标表示人数(所以,纵坐标人数不能出现小数)
①、此需求无论从哪里来说确实有必要;
②、想要纵坐标不出现小数,那么必须得人为的控制纵坐标
设置坐标轴的几个参数:tickCount、ticks、tickInterval 、 max、 maxLimit……等(参考:https://bizcharts.net/product/bizcharts/category/7/page/35),
将考虑使用maxLimit和tickCount
③、tickCount可以人为根据实际情况设定,所以问题转换为如何求得数据中的最大值?
思考:
数据中的最大值就是我们需要的tickCount吗?如果是tickCount/tickCount的值不为整数怎么办?又回到了最初的问题:有小数点!那如果不是,怎么设置能不会出现小数呢?
首先将数据排序(降序),然后再取排好序中数据的第一个即就是数据中的最大数,最后为数据中最大数加上某个数使得能够被整除。
1、首先通过JSON.parse()方法拿到目标数据——避免改变了源数据的顺序;
2、利用sort方法排序(降序);
3、取数组中数据最大值(即第一个值);
4、计算出符合要求的值
若 A%b!=0
则A%(A+(A-A%b)))=0
因此,数A加上 A减去A除以b的余数后,则A可以整除b
/**
* 返回数组中经过处理后的最大值
* @param arr
* @param pro
* @param tickCount
*/
export function getYAxisMax(
arr: any[],
pro: string,
tickCount: number,
): { yAxisMax: number; yInterval: number } {
if (arr && arr.length === 0) {
return { yAxisMax: 0, yInterval: 0 };
}
// 防止值传递引入改变原来数组的问题
const transArr = JSON.parse(JSON.stringify(arr));
// stpe1 排序 --降序
const temp = transArr.sort((a: any, b: any) => b[pro] - a[pro]);
// step2 取最大值(第一个值)
const max = temp[0][pro];
// step3 计算出符合要求的值
const t = tickCount - 1; // 从0开始,0算是第一条线
const yAxisMax = max % t === 0 && max !== 0 ? max : max + (t - (max % t));
const yInterval = yAxisMax / t;
return { yAxisMax, yInterval };
}
1、使用后源数据顺序发生改变?
sort方法会改变源数据的排序,所以需要通过JSON.parse来处理源数据
2、使用max不生效?
最初选用图表的max属性,实际测试中发现如果max等于数据中的max,则图表的max会自动变大,从而导致还是出现了小数;去官网测试了一波,发现还是如此,才改为了maxLimit
1、出现问题:
单纯的设置maxLimit,发现,数据中最大值小于maxLimit时,图表并不会按照预期的效果展示,所以,此方法有bug
2、解决方法:
方法一:
同时设置max和maxLimit,可行
方法二:
直接控制坐标轴的ticks,
方法一需要设置的属性太多了,而方法二只需要设置一个属性,所以,决定采用方法二。为了使工具类更方便使用,做了修改,修改后的代码:
/**
* 返回数组中经过处理后的最大值、interval、range
* @param arr
* @param pro
* @param tickCount
*/
export function getYAxisMax(arr: any[], pro: string, tickCount: number): any {
if (!arr || (arr && arr.length === 0)) {
return { yAxisMax: 0, yInterval: 0, yRang: [] };
}
// 防止值传递引入改变原来数组的问题
const transArr = JSON.parse(JSON.stringify(arr));
// stpe1 排序 --降序
const temp = transArr.sort((a: any, b: any) => b[pro] - a[pro]);
// step2 取最大值(第一个值)
const max = temp[0][pro];
// step3 计算出符合要求的值
const t = tickCount - 1; // 从0开始,0算是第一条线
const yAxisMax = max % t === 0 && max !== 0 ? max : max + (t - (max % t));
const yInterval = yAxisMax / t;
const result: number[] = [];
if (yAxisMax && tickCount && yInterval) {
for (let i = 0; i < tickCount; i += 1) {
result.push(i * yInterval);
}
}
return { yAxisMax, yInterval, yRang: result };
}
产品使我进步,需求让我成长,本来以为一切都大功告成了,万万没想到,需求稍微变一下,我就傻眼了
补充说明:
使用双轴坐标轴时,必须要将两边坐标轴对应的count设置一样,否则图表的grid会混乱,就算想避免这个问题,隐藏掉grid?两边的文字不能对其!隐藏其中一个的grid?你会发现,被隐藏grid那边的坐标轴label时而在图表grid上面,时而在grid下面,反正就是对不齐!所以,目前只有一个解决方案,两边的count设置成一样的!
你以为这样就可以了!不,这只是前提,说到底,上面的方法初衷就是为了解决双轴图表grid混乱的问题的!接下来,还需要设置坐标轴的最大值max,最小值min(一般默认是0)。
总结下:
对于双轴图表,解决思路如下:
然后就突然发现,咦?这不就是动态设置ticks吗,是的,所以,动态设置ticks也可(别忘记ticks的length要一样)
新的问题:
和同事终于讨论出解决方案了,产品的需求都达到了,激动的封装成工具类,并暗自高兴以后遇到这种需求就不用慌了,然而,测试小姐姐一个截图扔在群里,并附文字:我的数据为负数时,就成这样了(图像部分展示在横坐标一下)……
看了一下,哦!限定了min,好改呀,不就是删除代码的操作吗,然鹅,grid又混乱了,并且惊恐的发现:纵坐标为负数时,label中一定会有0,这就导致了,所以,0一下label的间距和0以上的label间距甚至0以上label的间距都不同!这怎么办?思考半天,无解,问同事,也没遇到,要么遇到单轴的,要么双轴的两边都值都大于0,我真实幸运的一塌糊涂,遇到的是双轴的,并且左边坐标轴可能为负数,而右边永远大于0,。
和同事讨论一波,发现如果非要设置min和max,那么当min小于0时,纵坐标的label无论如何都会有0,所以,重点是消除0,but,没找到相应属性。好吧!那只好人为设置ticks了!
加班测试了好几个小时,终于达成效果,见代码(很累赘,暂时就先这样了):
export function getYAxisMax(arr: any[], pro: string, tickCount: number): any {
if (!arr || (arr && arr.length === 0)) {
return { yAxisMax: 0, yInterval: 0, yRang: [] };
}
// 防止值传递引入改变原来数组的问题
const transArr = JSON.parse(JSON.stringify(arr));
// stpe1 排序 --降序
const temp = transArr.sort((a: any, b: any) => b[pro] - a[pro]);
// step2 取最大值(第一个值)
const max = Math.ceil(temp[0][pro]);
if (temp[temp.length - 1][pro] < 0) {
const min = Math.floor(temp[temp.length - 1][pro]);
const distance = Math.abs(min) + max;
// step3 计算出符合要求的值
const t = tickCount - 1;
const yAxisMax = distance % t === 0 ? distance : distance + (t - (distance % t));
const r = yAxisMax - Math.abs(min);
const yInterval = yAxisMax / t;
const result: number[] = [];
let min2 = min;
while (min2 < 0) {
result.push(min2);
min2 += yInterval;
}
result.push(min2);
for (let i = 1; min2 < r; i += 1) {
min2 += yInterval;
result.push(min2);
}
return { yAxisMax: yAxisMax - Math.abs(min), yInterval, yRang: result };
}
// step3 计算出符合要求的值
const t = tickCount - 1; // 从0开始,0算是第一条线
const yAxisMax = max % t === 0 && max !== 0 ? max : max + (t - (max % t));
const yInterval = yAxisMax / t;
const result: number[] = [];
if (yAxisMax && tickCount && yInterval) {
for (let i = 0; i < tickCount; i += 1) {
result.push(i * yInterval);
}
}
return { yAxisMax, yInterval, yRang: result };
}
有机会还是要优化下的
箱型图+折线图。要求:
1、纵坐标只显示一个(实际上应该有两个的,只好隐藏了);
2、纵坐标自适应(最小值不能为0);
解决思路:
1、隐藏右轴;
2、左右纵坐标设置相同的刻度;
封装方法:从数据中获取最大最小值,最终设置给纵坐标,使两边刻度一致
export function getYAxisData(arr: any[], pro: string, pro2: string, tickCount: number): any {
if (!arr || (arr && arr.length === 0)) {
return { min: 0, max: 0, ticks: [], interval: 0 };
}
// 防止值传递引入改变原来数组的问题
const transArr = JSON.parse(JSON.stringify(arr));
// stpe1 排序 --降序
const tempArr = transArr.filter((i: any) => i[pro] !== null);
// step2 取最大值(第一个值)
const max = Math.max(...tempArr.map((item: any) => item[pro]));
// step2 取最小值(最后一个值)
const min = Math.min(...tempArr.map((item: any) => item[pro2]));
const dValue = max - min;
// 一条线为最小值min===>实际上有tickCount-1
const dividend = tickCount - 1;
const realMax = dValue % dividend === 0 ? max : max + dividend - (dValue % dividend);
const interval = (realMax - min) / dividend;
const ticks = [];
for (let i = 0; i < tickCount; i++) {
ticks.push(i * interval + min);
}
return { min, max: realMax, ticks, interval };
}