简介

Scale 是数据映射的核心模块,主要完成数据映射到 0-1 空间,Scale 同坐标系、坐标轴、图例、Tooltip 等都相关,从 1.0 到 3.x 这个模块始终是非常稳定的模块,但是也遇到一些问题,4.0 正式发布之际,我们决定对 Scale 做一次重构,解决这几年遇到的一些问题:

  • ticks 的计算问题
  • scale 类型过少的问题
  • scale 的同步和引用

Ticks 的计算问题

ticks 用于在坐标轴上显示刻度,也用于 Legend 上显示映射关系,ticks 由几个方面决定:

  • 数据的字段类型
  • 数据字段的所有的值
  • 用户的约束,包括:数据值域、映射域、ticks 的个数、间距 等

结合过去几年用户询问最多的问题,在这次重构中我们重点解连续字段、时间类型和分类类型的 ticks 计算问题。

连续字段的 ticks 计算

连续字段的 ticks 计算面临的主要矛盾是:”自动计算出来的 ticks 同用户预期不一致“。虽然大多数场景可以可以通过增加配置项的方式来解决,但是一些数据来源不确定的产品上,很难手工的对配置项进行调整,主要的问题集中在三个方面:

  • min, max 限定后生成的 ticks 依然超出限制
  • ticks 个数做了限定,但是最终 ticks 个数误差比较大
  • nice 为了优化数值的范围,反而带来了很多误解

    计算 ticks 出现问题的原因

    假设计算 ticks 的条件如下:

  • 用户指定的数据范围是 [3, 96]([min, max])

此时生成的 ticks 的最理想的是 [0, 20,40, 60, 80, 100],但是默认的 tickCount: 5 ,所以生成的ticks 是 [0, 25, 50, 75, 100],如果要符合预期则要调整 tickCount: 6 。

如果这时候我们调整约束条件:

  • 数据范围 [0, 120]([min, max])
  • tickCount: 5

这时候 tickInterval = 120 / (5-1) = 30,所以可以生成 ticks: [0, 30, 60, 90, 120]。
如果要求 tickCount: 6, tickInterval = 120 / (6-1) = 24,所以生成 ticks 为[0, 24, 48, 72, 96, 120],但是由于 24 不是一个让人非常容易理解的数字,这时候 tickInterval 可能会调整成为 20、25、30:

  • 如果调整 tickInterval: 20,则ticks: [0, 20, 40, 60, 80, 100, 120], tickCount 则变为 7
  • 如果调整 tickInterval: 25,则 ticks:[0, 25, 50, 75, 100, 125], tickCount可以保持为 6 但是 max 大于了限定 120
  • 如果我们调整 tickInterval: 30 则 ticks: [0, 30, 60, 90, 120], tickCount 则变为 5

从上面的计算过程中我们可以看到:

  • min, max 的限定会约束不住
  • tickCount 会变大或者变小

    min, max 限定

    限定 min 和 max 同时使得第一个 tick = min, 最大 tick = max ,除非 tickInterval 允许整除,否则不可能做到,例如 min = 3, max = 97 时,[3, 21.8, 40.6, 59.4, 78.2, 97] ,所以这时候有两个方案:

  • min, max 不在 ticks 的范围内,则:ticks: [20, 40, 60, 80]

  • min, max 进行 round 处理,则 ticks: [0, 20, 40, 60, 80, 100]

d3 和 G2 都是使用 nice: true 来控制第一个和最后一个 tick 是否进行 round,而在有些算法中这个参数是 loose

ticks 个数的误差问题

从上面的分析来看 tickCount 也没法固定,仅仅能够降低误差,用户要求 5 个而我们产出 4-6 个,不能用户要求 5 个我们产生出 10 个,nice 的设置也会影响最终 ticks 的个数。

nice 问题

如果有些数据时存在天然限制的,例如:学生的考试分数必须在 0-100 内,温度不可能低于 -373度等,由于 nice 的存在,导致最小的 tick 和 最大的 tick 没有在约束内,这时候就需要用户自己指定 nice: false,并设置 min 或者 max。

时间类型的 ticks 计算

等分问题

保头保尾的问题

时间分类的问题

分类类型的 ticks 计算

分类个数太多的时候

Scale 的类型

有序分类的 Scale

分段 Scale 的支持

可断开的 Scale 支持

局部非连续的 Scale 支持

对称 Scale 的支持

Scale 的同步和引用

Scale 同步:不同数据源的一些字段需要统一度量,保证坐标轴、图例的显示一致
Scale 引用:从一个字段衍生出来的多个字段,例如:一个字段计算的平均值、最大值等

这部分已经在 Scale 同步 做了讨论,同时已经实现。

Scale 的实现

最终的类图如下:

Scale 重构 - 图1