(译)理解D3数据绑定

原文链接

本人全部译文地址

当你想要使用D3绘制散点图时,你需要在图中创建多个circle来表达你的数据信息。这时你会发现D3根本没有给你一个创建多个DOM元素的原生方法。

虽然它为我们提供了.append这个方法来创建单个元素。

svg.append('circle')
    .attr("cx", d.x)
    .attr("cy", d.y)
    .attr("r", 2.5)
复制代码

但这样每次只能创建一个circle元素,然而我们需要一堆圆来传达数据内容。在你想要使用for循环或者暴力解决这个问题前,请理解下面例子中的链式方法的意义。

svg.selectAll("circle")
  .data(data)
  .enter().append("circle")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })
    .attr("r", 2.5);
复制代码

上面的代码做了我们想做的:为每个数据元素创建了对应的circle元素,赋值xy属性定位到对应位置。在上面的代码中selectAll('circle')做了什么?为什么我们需要选择不存在的元素来创建新的元素?

这是约定俗成的写法,与其告诉D3如何来做事,不如直接说你想要什么。我们想要circle元素来对应数据信息,每个圆对应一份数据元素。为此我们告诉D3我们需要一个与数据对应的cirlce元素选择集,而不是要求D3创建circle元素。这个思想就是我们本次所要说明的数据绑定

当数据绑定到已存在元素上时,会创建update选择集。其余未被绑定到元素上的数据会创建enter选择集,表示缺失的元素。同样,已存在且未被绑定到数据的元素会返回exit选择集,通常表示需要被删除的元素。

下面我们通过解释代码来一步步说明enter-append模式数据绑定的意义:

  1. 首先,svg.selectAll('circle')返回一个全新的空选择集,因为SVG容器中是空的,这个选择集的父元素节点就是SVG容器本身。
  2. 接着这个选择集与一个数组进行数据绑定,返回三个表示三种状态的新选择集:enterupdateexit。因为这些选择集现在是空的,所以返回的updateexit也是空的。但是因为已经绑定数组了,所以此时enter选择集包含对应每个数据元素的占位符。
  3. 当执行selection.data方法时会返回update选择集,enterexit选择集不会返回。执行selection.enter则会返回对应的enter选择集。
  4. enter选择集上执行selection.append方法会在SVG容器中添加缺失的元素。现在在SVG容器中,每个数据项都有一个对应的circle元素存在了。

数据绑定的本质是先声明选择集和数据之间的关系,然后通过enterupdateexit三个状态来具体实现这个关系。

为什么这么麻烦?就不能提供一个直接创建多元素的原生方法吗?数据绑定的好处在于它可以泛化。当以上代码只是处理了enter选择集,这对静态可视化来说已经足够了。你也可以扩展代码让它支持动态图表,只需要稍微修改下updateexit选择集的操作。这意味着你可以可视化实时数据,允许交互探索,并可以平稳过度数据的变化。

下面是一个小例子:

var circle = svg.selectAll("circle")
  .data(data);

circle.exit().remove();

circle.enter().append("circle")
    .attr("r", 2.5)
  .merge(circle)
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; });
复制代码

每当运行此代码时,它将重新计算数据绑定并维护元素和数据之间所需的对应关系。如果此时新数据集数量少于老数据集时,剩余的元素将出现在exit选择集当中并且会被删除。如果新数据集中元素数量多于老数据集时,enter选择集会为这些数据生成占位符,并创建对应的circle元素。如果新数据集和老数据集数量相同,则现有元素会更新到新数据集对应的位置,没有元素增减。

以数据绑定的思想来编写意味着你的代码更具有声明性:你可以处理这三种状态无需任何条件语句或循环。相反,你可以描述元素应该如何与数据相对应。如果返回的enterupdateexit选择集为空,对应的代码是不会执行的。

如果有需要,数据绑定还允许你操作元素为特定状态。比如你可以设定给enter选择集的元素设定属性(例如圆半径,定义'r'属性)。

circle.enter().append("circle")
    .attr("r", 0)
  .transition()
    .attr("r", 2.5);
复制代码

后记

本文翻译自D3作者Mike Bostock2012年的博文,翻译本文的目的是希望可以从作者本身的角度来理解为什么要用绑定数据和元素的方式来完成创建多SVG元素的操作。

转载于:https://juejin.im/post/5b0e0c2c518825156010b0a0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值