浅析 BizCharts 双 Y 轴混合图刻度线对齐方法

引言

现存许多图表产品在双 Y 轴混合图刻度线对齐上还存在不足,例如下图展示的 Ant Design Charts 和 Echarts。但 BizCharts 不仅可以自动实现双 Y 轴刻度线对齐,还能做到两侧 Y 轴刻度线随交互变化而变化。

因此,开始探索 BizCharts 源码寻找答案,本期关注线性刻度线(相邻刻度间,刻度值差值大小相同)对齐方法。

实现猜想

在引入 BizCharts 实现双 Y 轴刻度线对齐方法前,我们不妨设想下,如果我们需要实现这个功能,应该如何实现?

分析问题:双 Y 轴统计图坐标轴对齐,本质是需要在页面上绘制 2 条坐标轴,坐标轴刻度条目和位置一致。从实现逻辑上看,我们需要获取 2 个等长的一维数组(设为 leftTicks 和 rightTicks),每一项都对应坐标轴的刻度值。

解决思路:

1、确定刻度值数目 n(可以从视觉美观上确定一个值,也可以支持外部传入,即由用户确定刻度值数目);

2、确定两侧 Y 轴上数值的最大和最小值;

3、小幅度调整左右 Y 轴数值的最值(在满足数值美观度和数据可覆盖的条件下,使数值范围能等间距地划分成 n 份);

4、基于最值,得到刻度值序列 leftTicks, rightTicks。

但如何小幅度调整 Y 轴两侧数值的最值呢?

BizCharts 实现方法

BizCharts 的实现方法和上文"实现猜想"列出的解决思路不同,它没有把解决"小幅度调整左右 Y 轴数值的最值"作为目标,而是提出了好的相邻刻度值间距(线性刻度下,刻度值是等差数列)nickTickList,并遍历寻找最接近真实数据平均刻度值(下文代码块的 avgInterval)的刻度间距,最终确定刻度最小值和刻度值序列。

简而言之,就是在理想刻度间距 nickTickList 中去找最接近真实数据刻度间距 avgInterval 的间距值 interval。

arduino 复制代码
// min和max为图数据的最值,tickCount 为刻度数目 
const avgInterval = (max - min) / (tickCount - 1);

最佳刻度值间距和刻度值数目

BizCharts 提出最佳刻度间距集合为下文代码块 SNAP_COUNT_ARRAY 及其以 10 等比例缩放的数据,即100,1000,...,0.1,0.01,0.001 等等都是最佳刻度间距。

ini 复制代码
const SNAP_COUNT_ARRAY = [1, 1.2, 1.5, 2, 2.2, 2.4, 2.5, 3, 4, 5, 6, 7.5, 8, 10];

对于刻度值数目,BizCharts 实现时默认刻度值数目为 5。

内部实现

我们将内部实现大致分为以下几步:归一化数据、计算平均刻度值、查找理想间距 interval、计算最小刻度值、范围校验。程序流程图如下所示:

归一化数据

此处采用归一化数据目的是将数据映射在 1~10 的范围,并采用 factor 记录数据缩放比例,方便后续查找刻度间距。

ini 复制代码
function getFactor(number) {
  // 取正数
  number = Math.abs(number);
  let factor = 1;
​
  if (number === 0) {
    return factor;
  }
​
  // 小于1,逐渐放大
  if (number < 1) {
    let count = 0;
    while (number < 1) {
      factor = factor / 10;
      number = number * 10;
      count++;
    }
    // 浮点数计算出现问题
    if (factor.toString().length > DECIMAL_LENGTH) {
      factor = parseFloat(factor.toFixed(count));
    }
    return factor;
  }
​
  // 大于10逐渐缩小
  while (number > 10) {
    factor = factor * 10;
    number = number / 10;
  }
​
  return factor;
}

计算平均刻度间距

平均刻度间距是指以真实数据最值作为线性刻度最值时的刻度间距,计算方法如下:

arduino 复制代码
/**
* max和min为真实数据的最值
* tickCount为刻度数目
*/
const avgInterval = (max - min) / (tickCount - 1);

查找理想间距 interval

按照从小到大的顺序遍历理想间距数组,定位最接近平均刻度间距的理想间距。

ini 复制代码
// 第一轮查找理想间距
let similarityIndex = 0;
  for (let index = 0; index < SNAP_COUNT_ARRAY.length; index++) {
    const item = SNAP_COUNT_ARRAY[index];
    if (calInterval <= item) {
      similarityIndex = index;
      break;
    }
  }
  const similarityInterval = getInterval(similarityIndex, tickCount, calMin, calMax);
​
// 递归函数:查找理想间距
function getInterval(startIndex, tickCount, min, max) {
  let verify = false;
  let interval = SNAP_COUNT_ARRAY[startIndex];
  // 刻度值校验,如果不满足,循环下去
  for (let i = startIndex; i < SNAP_COUNT_ARRAY.length; i++) {
    if (intervalIsVerify({ interval: SNAP_COUNT_ARRAY[i], tickCount, max, min })) {
      // 有符合条件的interval
      interval = SNAP_COUNT_ARRAY[i];
      verify = true;
      break;
    }
  }
  // 如果不满足, 依次缩小10倍,再计算
  if (!verify) {
    return 10 * getInterval(0, tickCount, min / 10, max / 10);
  }
  return interval;
}

计算最小刻度值

arduino 复制代码
/**
* min 真实数据的最小值
* interval 为理想刻度间距
*/
const minTick = Math.floor(min / interval) * interval;

刻度范围校验

判断刻度值范围(基于当前刻度数、理想刻度间距和最小刻度值计算得到)是否覆盖了真实数据。

markdown 复制代码
/**
* minTick 为最小刻度值
* tickCount 为刻度数目
* interval 为理想刻度间距
* max 真实数据的最大值
*/
if (minTick + (tickCount - 1) * interval >= max) {
   return true;
}

总结

BizCharts 在双 Y 轴刻度线对齐方法上主要是依赖了他们提出 nice 的刻度差值,将"小幅度调整最值"等范围无限化的问题给具体化了,解决问题的方式很新颖。

参考信息

github.com/alibaba/Biz...

作者:ES2049 / [小李爱吃车厘子] 文章可随意转载,但请保留此 原文链接。 非常欢迎有激情的你加入 ES2049 Studio,简历请发送至 caijun.hcj@alibaba-inc.com

相关推荐
fishmemory7sec3 分钟前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
fishmemory7sec5 分钟前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
豆豆1 小时前
为什么用PageAdmin CMS建设网站?
服务器·开发语言·前端·php·软件构建
twins35202 小时前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky2 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~2 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n03 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
昨天;明天。今天。3 小时前
案例-任务清单
前端·javascript·css
zqx_74 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架