D3 从入门到实战 - 4

1,373 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 27 天,点击查看活动详情

前因

上一节中,我们通过完成了一个 柱状图 的示例加深了对 SVG 操作的学习,其中我们通过循环 data 将柱状图渲染到画布中,如果这个数据是动态的,那我们要如何操作才能将数据动态的渲染到画布中呢?今天我们就一起接着来学习一下吧!

Data - Join

本质上是将数据与图元进行绑定,从而实现数据变化后渲染层也跟着变化。通过使用 Data-Join 可以省去大量跟据数据设置图元属性的代码量,并且对于动态变化的数据能够提供统一的接口。

那么我们该如何动态的修改数据呢?其实很简单,D3 给我们提供了一个简单的 API -- data,通过它就能够动态的修改数据,代码如下:

d3.selectAll('rect').data(data, (item) => item.name).attr('width', (item) => xScale(item.value));

当我们通过 data 方法设置新的数据时,就能够进行动态的修改数据。data 有两个参数,第一个参数是当前要操作的数据,第二个参数是一个可选函数,主要用于记录当前更新的key,这主要是为了在后续更新中确保更新值的顺序与最初设置的保持一致。

我们通过 Data-Join 可以实现以数据为中心的可视化操作,并且根据数据的属性自动调整绑定图元的属性,从而不再需要我们手动进行添加、修改、删除图元,因为 D3 会根据 Data-Join 的绑定自动进行推断。那如果图元的数目不等于数据的条目会怎么样呢?它会根据数据条目的数量选定相应数量的图元进行渲染。

数据更新

在前面我们说 D3 会根据 Data-Join 自动更新数据,那么它是如何做到自动更新的呢?

D3 内部绑定的数据有三个"状态",其中 Update 在图元和数据条目相同时,才会进行单存的更新;而当数据的条目多余图元甚至没有图元时,通常更新都是用于第一次绑定的数据,也就是使用 Enter;最后,当数据的条目少于图元甚至没有数据时,通常用于结束可视化,即 Exit

Enter

当我们第一次渲染画布时,可能就会遇到有数据却没有图元的情况,那么 D3 会自动“搞清楚”哪些数据是新增的,它会根据新增的数据自动生成相应的图元,这就是 Enter 的作用。当第一次生成图元后,它会自动进行占位,占位的内容需要开发人员自行添加,通过 append 方法。代码如下:

const p = svg.selectAll('.class').data(data).enter().append('p').attr(...);

上述的代码中,我们通过 enter 方法给不存在的数据提供一个占位符,这样后面才能使用 append 去创建对于的元素。

Update

当已经有图元且也有数据时,我们如果要更新当前的画布,用到的就是 UpdateUpdate 作为实际可视化任务最常用的状态,经常被单独封装成一个函数来进行使用。代码如下:

const p = svg.selectAll('.class').data(data).attr(...).attr(...)

在实际的开发中,我们并不需要显示的调用 Update 方法,就如上面说的一样 Update 是最常用的状态,也是 D3 中默认的更新方法。Update 也经常与 D3 中的动画一起使用,我们可以通过 transition().duration() 来实现动画的效果,其中 duration() 接受一个参数,指当前动画执行的时长,单位是毫秒,大致的代码如下:

d3.selectAll('.class').data(data).transition().duration(1000).attr('width', item => xScale(d.value));

我们通过 transition().duration(1000) 设置动画,执行时长为 1000毫秒,然后通过 attr 来改变 柱状图 的宽度,最终实现的效果就是 柱状图 中的”柱子“会根据动画在1毫秒内改变宽度。

Exit

当我们更新的某条数据被删除时,也就是有图元但是数据没了,D3 就会自动"搞清楚"哪些图元是没有绑定数据的,也就是会执行 Exit 状态。代码如下:

const p = svg.selectAll('.class').data(data).exit().remove();

上述的代码中,当我们在执行 exit 方法后,D3 就会自动释放已经不存在的图元,然后我们再通过 remove 方法将相关的内容移除掉,当然你也可以通过 attr 修改不存在的数据的颜色或者透明度,这些都是可以的。

Data - Join 的简洁形式

在上面讲到 Data - Join 时,我们是通过 data 方法来实现数据的绑定与更新,其中需要有三种状态需要我们自己注意,除了 Update 状态是默认的,EnterExit 就需要我们自己手动的添加,那有没有一种更简洁的方式呢?答案是有的,那就是直接使用 data(...).join(...) ,它默认 enterupdate 的执行形式相同,并且默认 exit 是删除节点,这样就不需要我们自己手动操作了。默认的 Data - Join 使用起来虽然简洁,但是不够灵活,我们必须要自己设置 enter 数据的初始图元数据,update 会每次重新设置初始值,从而导致动画出现”奇怪“的效果。

我们使用 data(...).join(...) 就可以完成相关的 ”定制“, 大致的代码如下:

const p = svg.selectAll('.class').data(data).join(
    enter => enter.append('text').attr('fill', 'red').text(item => item), 
    update => (), 
    exit => ()
)

最后

在这一节中,我们了解到如何动态的修改数据,以及 D3 修改数据的几个状态,包括 EnterUpdateExit,以及它们的使用方式,在下一小节中,我们通过一个实际的案例再来加深对本节内容的学习,让我们一起加油吧!

未完待续...

最后,如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家

往期回顾

D3 从入门到实战 - 1

D3 从入门到实战 - 2

D3 从入门到实战 - 3