当前位置: 首页 > 工具软件 > BizCharts > 使用案例 >

Bizcharts之纵坐标label不能是小数&&双轴坐标轴

茹照
2023-12-01

一、需求

     使用某图统计某学校最近几年人数变化情况,横坐标年份,纵坐标表示人数(所以,纵坐标人数不能出现小数

二、实现思路

    ①、此需求无论从哪里来说确实有必要;

    ②、想要纵坐标不出现小数,那么必须得人为的控制纵坐标

        设置坐标轴的几个参数: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)。

总结下:

  对于双轴图表,解决思路如下:

  •   两边tickCount一致
  •   需要设置min和max

然后就突然发现,咦?这不就是动态设置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 };
}

 

 类似资料: