d3源码之d3-scale
要尝试把之前用d3画的东西放到react-native上, 而rn是没有dom只有svg的lib, 那么就要研究下d3的实现了.
背景&目标
之前用d3做了一个事件时间线, 用到了d3-scale
, d3-brush
, d3-selection
. 那么在rn上无法对dom(其实是svg)进行拿起来干
式的操作, 那么想在rn上模仿一个类似的时间线就要去了解一下d3的实现了.
所以决定从最简单的d3-scale
开始, 这个lib算是个数学库, 不涉及dom操作, 在时间线的项目中使用到的api也不多, 那么目标就设为了解这几个api的运行流程.
d3.scaleLinear()
.domain()
及.range()
scale()
及scale.invert()
我也fork了一份代码用来写注释.
目录结构
d3应该是从大而全拆成各个小module再互相引用的, 用了rollup, 从rollup.config看到入口是在根目录下的index.js
, 而具体内容在src
文件夹下, index.js
的内容全是export {xx as xxx} from './src/xx'
. 这个目录结构相当简单, 适用于d3的所有小模块的.
scaleLinear
我们的目标是d3.scaleLinear()
. 所以来到src/linear.js
. 完整版从这里看, 截取export的地方贴一下:
1 | export default function linear() { |
从这个输出结构看出, 输出的是scale
对象(应该是一个方法). 因为这里是linear, 所以对scale做了处理:linearish
, 字面意思”线性化”, 所以结论是不同的scale的核心是同一个工厂, 再经过不同的包装重载一些方法来输出不同的scale.
那么我们要研究的就是: continuous(deinterpolate, reinterpolate)
. 这三个变量何去何从都待我们一个个看过来.
deinterpolate, reinterpolate
deinterpolate:
来源: countinous的deinterpolateLinear
.
1 | export function deinterpolateLinear(a, b) { |
reinterpolate:
来源: 另一个模块: d3-interpolate
的interpolateNumber
.
1 | export default function(a, b) { |
代码很简单, 注释也写了, d3这个库有个特点就是花里胡哨, 而且各个小模块的花式还不同, 作者应该是在尝试各种招式~ 来总结一下:
这两个函数都接受两个参数, 是实际范围. 这两个函数都是在实际范围和x在范围内的位置做转换. 位置用数字来表示百分比.
deinterpolate返回的函数接收参数x: 实际点, 返回点在范围中的位置, 用0~1来表示.
reinterpolate返回的函数接收参数t: 位置, 返回这个位置对应的点.
后来发现在continuous的代码中有注释:
1 | // deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1]. |
continuous
完整版源码在这里. 函数是下面这个样子的, 注释也贴上了:
1 | export default function continuous(deinterpolate, reinterpolate) { |
归纳:
- continuous()调用返回值是一个方法.
- 因为闭包, 所以返回的这个方法里保存了一些属性: domain, range, clamp等.
- 返回值是
scale()
, 就是我们使用的比例尺. - scale()相对的是scale.invert(), 使用的是同一个生成函数.
- 每次通过方法改变scale的属性(domain, range, clamp等)就会触发rescale().
- rescale()的作用两个: 根据domain和range的维度来改变scale使用的函数; 重置缓存(因为属性不变输出是不变的所以不触发rescale()再次调用scale()不会重新计算).
另外:
- 我们使用场景domain和range维度都是2, 所以都用了
bimap
这个方法. - 学到一个奇怪的用法: function还可以有自己的键值, 因为
(function () {}) instanceof Object === true
吧.
pow
看完以后来看了一下scalePow()是如何实现的.
1 | function raise(x, exponent) { |
同样的返回值是continuous(deinterpolate, reinterpolate)
.
只是重写了deinterpolate
和reinterpolate
.
总结
d3-scale可以说是教科书式的工厂模式, 一个核心方法, 通过重写参数来提供不同api.
scaleLinear()的过程是:
- scaleLinear()返回值是一个带有内部属性的对象, 表面自己就是可以直接调用的方法.
- 通过一些方法来设置内部属性. domain和range默认是[0, 1].
- 核心算法就是加减乘除的比例尺. 通过domain和range和策略来输出结果.
(本文完)
如果你觉得本文对你有帮助, 你可以请我喝一杯咖啡
本文遵循cc协议
你可以在注明出处和非商用的前提下任意复制及演绎