D3.js 秘技

目录

制作 D3.js 动画

transition 的名字

客製动画

数据绑定与元素集合

绑定规则

阶层元素

属性设定

Style v.s. Attr

利用函数取值

Encore: d3.scale 的神奇秘技


制作 D3.js 动画

transition 的名字

D3.js 提供 transition() 函数供我们作动画,相当的方便,但是当我们需要多个动画一起执行时,该怎麽办?若我们对同一个物件调用的 transition 时间有重叠,后者会将前者完全取代掉,如下例我们根本不会看到红色出现,只会看到边框变色:

  d3.select("rect").transition().attr({ fill: "red" });
  d3.select("rect").transition().attr({ stroke: "green" });

如果我们改用串连调用的方式,两者则会依序呈现,而非同时进行:

  d3.select("rect")
    .transition().attr({ fill: "red" })
    .transition().attr({ stroke: "green" });

事实上, transition 是可以命名的,而且只要将各个 transition 命名,他们就可以同时执行,不会互相取代。如下例,在调用 transition() 时以名称为参数即可:

  d3.select("rect").transition("my-fill").attr({ fill: "red" });
  d3.select("rect").transition("my-stroke").attr({ stroke: "green" });

以下图为例,左右两个矩形各自以五个互相重叠的 transition 制作相同动画,包含宽、高、填满、边框与边线宽,左边使用未命名的 transition 而右边的 transition 各自有不同的名字。可以看到左方的动画互相重叠导致大部份都无法完成,但右方的动画则顺利做完。

这招在需要独立对不同属性做动画时特别有用。

客製动画

我们依靠 d3.transition 做动画时,其底层是基于 d3.interpolate 内插函数在运作的,也因此我们所能做的动画类型受到了他的限制。当我们想做更复杂的动画时,我们当然可以自行利用 JavaScript 与浏览器所提供的 setTimeout 或 requestAnimationFrame 等函数来做,但这不仅繁琐,动画一多程序逻辑也复杂了起来。

d3.transition 其实提供了客製动画的选项,我们可以利用 d3.transition.tween 函数来做!tween 函数接受一个 animation factory, 我们在里面产生动画处理器,并利用传入的动画进度参数来计算动画值:

  d3.transition().tween("动画名", function() {
    return function(progress)  {
      return progress; 
    };
  });

在上列程序码中,红色的函数会不断的被调用,其参数 progress 则会由 0 至 1 不断的被带入, 0 代表动画开始, 1 则代表动画结束。在这里面,我们可以应用各式各样的视觉效果,例如根据 progress 来更新圆饼的比例,做出变大圆饼的效果:

  var arc = d3.svg.arc().innerRadius(0).outerRadius(50);
  d3.select("path").transition().tween("growth-pie", function() {
    return function(progress)  {
      d3.select(this).attr({
        d: function(progress) {
          return arc.startAngle(0).endAngle(progress * Math.PI * 2);
        }
      });
    };
  });

下图即为利用 d3.transition.tween 做出的圆饼图动画,三个圆饼分别使用不同的 easing 函数:

数据绑定与元素集合

绑定规则

用过 D3.js 的人都知道 D3.js 最核心的逻辑在于数据与元素的绑定,比方说下例我们将 1 ~ 5 的数字与 path 结合:

  d3.selectAll("path").data([1,2,3,4,5]);

这边结合是照数据在阵列中的顺序,也就是说若我们之后更新数据时、顺序有变化,数据就不会绑到原先的元素上,而是绑到其它的元素上了。这有时会造成视觉呈现上的问题。该怎麽解决呢?

我们只要指定绑定的规则即可,在 data() 中再加上一个规则函数:

  d3.selectAll("path").data([1,2,3,4,5], function(it) { return it; });

该函数的传回值即是绑定规则;当数据与元素有着相同规则时,两者就会被结合。这类似数据库系统 Table Join 时指定特定栏位做线索的概念。

阶层元素

数据绑定后我们可以在设定样式时使用:

  d3.selectAll("g").data([1,2,3,4,5]);
  d3.selectAll("g").attr({
    width: function(d,i) { return d * 100; }
  });

上例中,每个 <g> 得到红色的 d 的数值都不一样,分别会是 1 ~ 5 。那请问大家,如果 <g> 下面还有元素,该元素要怎麽利用 <g> 绑定的这个数字呢?比方说文件的结构像是下面这样:

  <g><rect/></g>
  <g><rect/></g>
  <g><rect/></g>
  <g><rect/></g>
  <g><rect/></g>

若我们利用 d3.selectAll(“g rect") 想要来设定 rect 的宽度 …. 事实上, D3.js 在读取数据时,会用向上搜寻的方式寻找。在此例中,<rect> 并没有与任何数据绑定,因此 D3.js 会向各个 <rect> 的父元素 <g> 询问,这时因为 <g> 有绑定数据,于是这些数据就传回给 <rect>  供作使用了。

属性设定

Style v.s. Attr

因为 D3.js 与 SVG 的紧密结合,我们对 SVG 不够了解的话有时会造成很大的困扰。其中一个问题是这样的:SVG 元素可以用  CSS 设定样式,例如:

  <rect style="fill:red"/>

将矩形用红色填满,看起来很棒是吧?但 fill 其实是 rect 的属性之一,所以我们也可以这样写:

  <rect fill="green" style="fill:red"/>

请问此时这个矩形会是红色还是绿色呢?此外,矩形的参数 rx 与 ry 可以设定矩形圆角,我们可以把他写到 style 里去吗?如下:

  <rect fill="green" style="fill:red;rx:10;ry:10"/>

事实上,SVG 有所谓的「Presentational Attributes」,例如像是 fill 、 stroke 、 stroke-dasharray 等等的属性;这一类的属性可以放在 style 中,因此可以利用 CSS Selector 、 CSS Animation 来控制。然而,其它属性像是 rx 、 ry 、 圆的 cx 、 cy 、 r 都是不能通过 CSS 控制的属性。

接着,设定 Presentational Attributes 时, CSS 设定是优先于属性设定的,因此若我们利用 CSS 对 <rect> 设定了红色填满,再用属性设定绿色填满,结果还是会得到红色的矩形。若对这点不了解,有时利用 D3.js 操作属性时就会碰到怎麽设定都不会动作的窘境:

  d3.selectAll("rect")
    .style({ fill: "red" })
    .attr({  stroke: "green" });

这点千万要注意阿!

利用函数取值

在设定元素样式时,我们可以利用函数来指定其值,例如下例中红色的函数在要设定 <rect> fill 值时会被调用,其传回值「red」则会被填入:

  d3.selectAll("rect").attr({ fill: function() { return "red"; });

事实上不光是设定属性,D3.js 里面很多放参数的地方都可以用函数替换,例如 d3.selectAll :

  d3.selectAll(function() { return "rect"; });

Encore: d3.scale 的神奇秘技

d3.scale 用来做座标转换相当方便,他也可以将数值转换成颜色,像是利用 d3.scale.category20 或是直接做线性内插,相当的方便:

  d3.scale.linear().domain([0,1]).range(["#f00","#0f0"]);

上例可以帮我们把 0 ~ 1 之间的值用内插的方式对换到红色与绿色之间的值。好用吧!不过,这个大家都知道,不是我们要讲的秘技。事实上, d3.scale 底层是使用 d3.interpolate 实现,所以 d3.interpolate 所能内插的东西, d3.scale 都支持,例如阵列:

  d3.scale.linear().domain([0,1]).range([[0,1],[1,0]]);

或者歌词:

  d3.scale.linar().domain([0,1]).range([
    "5 little ducks, went out one day ....",
    "1 little ducks, went out one day ...."
  ]);

希望这没有让你的脑袋炸掉。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值