(TODO)D3.js-比例尺

该文章内容摘抄自:https://segmentfault.com/a/1190000011006780

概念

一个函数,可以将某一区域的值映射到另一区域,其大小关系不变。

比例尺,很像数学中的函数。例如,对于一个一元二次函数,有 x 和 y 两个未知数,当 x 的值确定时,y 的值也就确定了。

在数学中,x 的范围被称为定义域,y 的范围被称为值域

D3 中的比例尺,也有定义域和值域,分别被称为 domain 和 range。开发者需要指定 domain 和 range 的范围,如此即可得到一个计算关系。

interpolotor

分类

连续性比例尺

d3.scaleLinear线性比例尺

使用d3.scaleLinear()创造一个线性比例尺,而domain()是输入域,range()是输出域,相当于将domain中的数据集映射到range的数据集中。

1
let scale = d3.scaleLinear().domain([1,5]).range([0,100])

映射关系:

img

接下来,我们来研究这个比例尺的输入和输出。

1
2
3
scale(1) // 输出:0
scale(4) // 输出:75
scale(5) // 输出:100

刚才的输入都是使用了domain区域里的数据,那么使用区域外的数据会得出什么结果呢?

1
2
scale(-1) // 输出:-50
scale(10) // 输出:225

所以这只是定义了一个映射规则,映射的输入值并不局限于domain()中的输入域。

d3.scaleQuantize量化比例尺

d3.scaleQuantize()也属于连续性比例尺。定义域是连续的,而输出域是离散的。

1
let scale = d3.scaleQuantize().domain([0, 10]).range(['small', 'medium', 'long'])

映射关系:

img

输入与输出:

1
2
3
scale(1) // 输出:small
scale(5.5) // 输出:medium
scale(8) // 输出:long

而对于domain()域外的情况:

1
2
scale(-10) // 输出:small
scale(10) // 输出:long

大概就是对于domain()域的两侧的延展。

序数比例尺

d3.scaleBand

d3.scaleBand()并不是一个连续性的比例尺,domain()中使用一个数组,不过range()需要是一个连续域。

1
let scale = d3.scaleBand().domain([1,2,3,4]).range([0,100])

映射关系:

img

看一下输入与输出:

1
2
3
scale(1) // 输出:0
scale(2) // 输出:25
scale(4) // 输出:75

当输入不是domain()中的数据集时:

1
2
scale(0)  // 输出:undefined
scale(10) // 输出:undefined

由此可见,d3.scaleBand()只针对domain()中的数据集映射相应的值。

d3.scaleOrdinal

d3.scaleOrdinal()的输入域和输出域都使用离散的数据。

1
let scale = d3.scaleOrdinal().domain(['jack', 'rose', 'john']).range([10, 20, 30])

映射关系:

img

输入与输出:

1
2
3
scale('jack') // 输出:10
scale('rose') // 输出:20
scale('john') // 输出:30

当输入不是domain()中的数据集时:

1
2
scale('tom') // 输出:10
scale('trump') // 输出:20

时间比例尺

d3.scaleTime

1
2
3
4
5
6
let scale = d3.scaleTime()
.domain([new Date(2017, 0, 1, 0), new Date(2017, 0, 1, 2)])
.range([0,100])

scale(new Date(2017, 0, 1, 0)) // 输出:0
scale(new Date(2017, 0, 1, 1)) // 输出:50

d3.scaleUtc

依据世界标准时间(UTC)来计算

颜色比例尺

10就是10种颜色,20就是20种:

1
2
3
4
5
6
7
d3.schemeCategory10
d3.schemeCategory20
d3.schemeCategory20b
d3.schemeCategory20c

// 定义一个序数颜色比例尺
let color = d3.scaleOrdinal(d3.schemeCategory10)

其他比例尺

1
2
3
4
5
d3.scaleIdentity() // 恒等比例尺
d3.scaleSqrt() // 乘方比例尺
d3.scalePow() // 类似scaleSqrt的乘方比例尺
d3.scaleLog() // 对数比例尺
d3.scaleQuantile() // 分位数比例尺

技巧

如何实现逆向比例尺?

D3中提供了invert()以及invertExtent()方法可以实现这个过程:

1
2
3
4
5
let scale = d3.scaleLinear().domain([1,5]).range([0,100])
scale.invert(50) // 输出:3

let scale2 = d3.scaleQuantize().domain([0,10]).range(['small', 'big'])
scale2.invertExtent('small') // 输出:[0,5]

不过,值得注意的是,这两种方法只针对连续性比例尺有效,即domain()域为连续性数据集的比例尺。那么非连续性的比例尺就没有invert()方法了吗?

也是有办法的,比如d3.scalePoint的逆向,就可以参考这个文章的方式:

https://stackoverflow.com/questions/40573630/how-can-i-implement-an-invert-function-for-a-point-scale

1
2
3
4
5
6
7
8
9
10
11
/**
* 根据x坐标获取日期
* 注意这里的x是相对于整个页面的x,一般是事件触发点的event.pageX
* @param {*} x
*/
getDateByPageX(pageX) {
const domain = this.xScale.domain();
const range = this.xScale.range();
const rangePoints = d3.range(range[0], range[1], this.xScale.step())
return domain[d3.bisect(rangePoints, pageX) - 1];
}

怎么实现负对数比例尺?

将数据处理成线性数据再作图

这是ZHQ想出来的方案,太赞了,这样大大提升了我们组件的扩展性。

参考资料

(精)常用的3种比例尺的可视化讲解:

https://segmentfault.com/a/1190000011006780