Animations and transitions

  在交互式可视化中,有一个词叫reactive,指的是以可视化的方式来响应用户的行为,帮助用户进行可视化并理解其结果。这个颇有用。那如何来实现这种交互呢?经过动画。html

  若是处理得当,动画能够展示出不错的可视化交互数据...react

  是怎样的呢?git

  • 交互能够有效地提供用户的反馈。咱们能够知道自上次用户操做以后发生了什么变化?若是屏幕上的内容从一种状态变成另外一种状态,动画可使这一过程更加明显,突出而更具含义。另外,当须要显示任何形式的实时数据时,动画是必不可少的。
  • 动画可使人们更加关注图表的重要部分。咱们的视觉对运动很是敏感,所以引入和使用这些transitions能够大大地帮助咱们更加容易地从图表中获取到有用的信息。

    比较下面两个图表:github

    哪个更能让用户注意到柱状图中最后的那一个柱子呢?编程

    [说明:以上两个图使用了相同的model。点击按钮能够开始一个动画。若是图中内容为空,点击按钮能够显示图表。]浏览器

  • 动画能够和隐喻一块儿使用,好比增大,展开,移动,缩小等等。因此它能够真正提升可视化的表现力,并试图传达任何要表达的意思。

  这就是说,动画也确定会破坏你的可视化。这里有三个广泛问题。app

  • 动画很是突出。把注意力放在图表的一个特定的,明确的部分是很好的。可是当动画太多时会发生什么呢?没有其它的提示,用户很难将注意力集中到他们须要关注的地方。
  • 动画跨越了多种状态(如动画数据视频),使得它们难以相互比较,这不一样于咱们将各类不一样状态的静态图并排显示出来能够很容易地进行比对。
  • 若是动画不连续,又或者图表突然莫名其妙地消失,这就会致使一个变化盲视,从而大大地抵消了你但愿从动画中能得到的效果。

    看这个例子。svg

    动画过程当中线通过了一个空白状态,从而使咱们很难去跟踪原始状态和最终状态之间的变化。找出变化的惟一方法是将注意力集中到一个点上,而后记住它的原始位置,但这样作效率过低。函数

如今咱们怎么作呢?

  咱们已经看到动画在数据可视化中的做用了。如今咱们来作吧!咱们使用d3,它提供了多种数据动画的方式,使用它实现动画效果会相对容易一些。动画

原则

  若是你知道如何在d3中绘图,你就知道如何实现动画。(若是你还不知道,Alignedleft有一个精彩的教程集,能够帮助你如何开始,d3站点也列出了不少,包括一些提供的。)出于某种缘由,动画在d3中被称之为transitions。动画在技术上被定义为,对象的一个或多个特征在通过一段时间以后,从一个值过渡(transitions)到了另外一个值。

  那么咱们所说的特征指的是什么呢?它基本上表明了任何能够用数字表示的东西。

几个transitions的例子

  不出所料,当你随着时间来平滑地更新item的位置时,它就移动了。在svg中,对大多数形状而言位置是肯定的,例如咱们这里的蓝色矩形,它的位置由属性x和y的值来肯定,对应于形状的左上角。对于圆形,使用cx和cy或者中心点的坐标来肯定位置。对于路径,例如咱们的红色三角形,实际上经过"d"属性指定了全部点的位置。

  一样,当你改变大小时,对象会增大(或收缩)。你可使用width(宽)和height(高)来肯定矩形的大小,或者使用r(半径)来肯定圆形的大小。

  颜色也是一个数字属性,从一种颜色过分到另外一种颜色也是可能的(这个也颇有用)。在svg中,颜色是由fill或stroke定义的样式属性。

  与颜色同样,改变透明度也颇有用。当opacity被设置为0时,对象是彻底透明的。因此要实现对象的淡入淡出效果,须要对opacity属性进行transition操做。

如何实现

  如今咱们已经看到transitions能够用来些干什么了,让咱们来看看如何用d3来编写代码。

  咱们回到第一个例子。咱们尽可能简单一点。

  在d3中要建立一个像这样的方块,咱们能够这样写:

var mySquare=svg.append("rect")
  .attr("x",60)
  .attr("y",60)
  .attr("width",60)
  .attr("height",60);

  4个属性,很简单。

  若是你想让它移动到右边,只须要更新属性x的值。像这样:

mySquare
  .transition()
  .attr("x",320);

  就是这么简单:使用transition方法,而后指定你想要改变的值,就像建立一个新对象同样。经过这种方式,咱们能够很容易地实现上面任何一个例子的效果。

mySquare
  .transition()
  .attr("width",120); // 将会变大
 
mySquare
  .style("fill","white") // 若是fill的值一开始是空白,而后再指定样式,那么动画将从黑色开始 
  .transition()
  .style("fill","blue");
 
mySquare
  .transition()
  .style("opacity",0);

  咱们的例子并不是如此。Transitions发生在event以后,即当用户点击按钮时才开始动画。事实上,transitions一般会与事件和交互关联在一块儿。不过这并不复杂。咱们能够这样写:

button.on("click", function() {
  mySquare.transition().attr("x",320);
})

  如今,咱们的动画仅当按钮被点击时才开始。很明显,因为transition是在一个函数内部,因此咱们能够经过编程来决定方块走向哪里。不过这个例子咱们仍是让它简单一点。

动画102

  到目前为止,咱们已经看到了如何在d3中实现一些简单的动画,甚至进行一些交互。正如咱们所看到的,动画的实现方式和建立元素同样简单。好消息是,d3中的transitions很是灵活,同时也能够经过不少的技巧来进行自定义,而不用编写很复杂的代码。咱们更多须要知道的是该如何来作。

  在使用transition()方法以后,咱们能够指定一个duration和delay的值。Duration是transition将要持续的毫秒数,而delay是动画在执行以前须要等待的毫秒数。写法:

mySquare.transition()
  .attr("x",320)
  .duration(1000) // 持续时间为1秒
  .delay(100)     // 延迟0.1秒执行

  默认的duration是250ms,没有delay。

  我发现250ms的duration时间有点过短了。大多数时候,咱们都但愿动画要明显,我本身常常将duration的值增长到500或1000.除非有特殊缘由,动画的持续时间不该太长。若是你使用它们来可视化数据,你确定不但愿动画要花好几秒才将数据都显示出来。观察下面这两个例子(点击按钮开始)。

  第二个是否是会让人抓狂?你很难相信它纯粹浪费了你25秒的时间。

  缓动是一个技术名称,它其实是一个将时间转换为属性值变化的函数。在前面的例子中,你可能已经注意到了,一开始的时候值变化得很慢,而后加快,而后又变慢。是的,这代表你可使用不一样的函数来得到不一样的结果。我这里的例子只给出了3个缓动函数,而事实上还有不少的缓动函数。你能够本身编写缓动函数,这个不在本文的讨论范围。

  写法相似于这样:

mysquare.transition()
  .attr("x",320)
  .ease("elastic")

   (顺便说一下,修改属性和指定动画方式的顺序没有任何影响,你能够先使用.ease,后使用.attr)

  对于路径对象,经过transition来更新每个点的位置,你能够有效地改变路径的形状。这对于线图或任何一个路径图来讲特别有意思。

  像这样,若是你绘制的值发生了变化,你能够轻易地发现这些变化。相反,若是你只是清除并重绘,那么就很难发现数据的变化。在这几个例子中,路径的属性"d"的值被修改了(因此它们与最简单的例子本质上是不一样的)。

  有时(事实上常常会有),你但愿在一个transition完成以后立刻启动另外一个transition。然而下面这种方式不起做用:

mysquare.transition()
  .attr("x",320);
mysquare.transition()
  .attr("y",200);

  你可能会认为方块会向右移动,而后再向下移动。但事实并不是如此,它会开始一个向右的移动,而后紧接着启动第二个transition使其向下移动。因为这两个transition的duration相同而且都没有delay,所以第二个transition的效果更明显。

  若是第二个transition有一个delay,而且比第一个transition的duration要小,那么第一个transition会持续一段时间直到第二个transition的delay时间到期。而后,第二个transition会接管第一个transition开始执行。然而这并非你想要的,由于第一个transition完成到什么程度彻底取决于用户的机器和浏览器等,而这些都是不可预知的(看下面一段的解释)。

  那么给第二个transition一个精确的delay使其可以恰好对应上第一个transition的duration如何呢?这一般是能够的,可是delay和duration的值并不是十分精准。启动一个transition须要必定的时间(在个人机器上大约须要15毫秒,但可能会有变化),所以咱们很难经过这种方式将两个transitions无缝地链接起来。

  在更加复杂一点的程序中,有时候好几个事件会尝试对同一个对象触发transition。在这种状况下,第一个过程会被启动而后运行,直到另外一个transition开始。第二个transition会取代第一个transition。这意味着在第一个transition中被改变的属性值将会保留到第二个transition开始,这个值处于开始值和目标值之间。

  若是你想要确保全部的transitions都能将其属性更新为要达到的值,那么你可能须要在第一个transition的后续transitions中从新指定属性值,就像这样:

mysquare.transition()
  .attr("x",320);
mysquare.transition()
  .delay(250)
  .attr("y",200)
  .attr("x",320); // 即便第一个transition没有执行完,这里也会将x更新为320。

  这里还有一个方法能肯定将两个transitions链接起来。使用下面这种写法,一个事件能够精准地在一个transition结束时开始。这个事件能够是另外一个transition(上面的例子就是这样)。

mysquare
 .transition() 
 
 ...
 
 .each("end", function() { ... });

  这里,由.each("end")引入的最后一行的回调函数中的内容会正好在transition结束时被触发。

  那要怎么作呢?这里有3种常见的场景。

  (顺便说一句,这里的例子和前面的例子没有什么不一样,仅仅只是为了方便查看)。一种状况是在同一个item上启动另外一个transition。这里,方块会向右移动,而后向下移动。

  这里是实现的代码:

mysquare
  .transition()
  .attr("x",320)
  .each("end",function() { // 正如上面所说的,这是一个新的transition对象
    d3.select(this).       // 这里咱们还能够有另外一个.each("end") 
      transition()         
        .attr("y",180);
   });

  另外一种状况是在transition执行完后删除对象。这个颇有用,特别是在建立大量临时对象时。一个有趣的组合动画是,当你将透明度一直减到0,使对象不可见,若是你再也不须要它了,那么你就可使用remove()方法来删除它。

mysquare
  .transition()
  .attr("x",320)
  .each("end",function() { 
    d3.select(this).       // 如上所述,这里咱们将对象删除了
      remove(); 
   });

  最后,咱们能够建立一个新对象。咱们能够经过这种方式添加一个特效。下面是一个例子:

  这里,在transition结束时,建立了一个圆形,随后在圆形上开始了一个transition,并将透明度减小为0,而后删除该圆形。

  这是最后一个带有多个效果合并的例子。

进一步

  信不信由你,咱们只是接触到了d3动画一些很是基础的东西。

  还有两个transition的用处咱们没有讲到,由于稍微有点复杂,因此这里我只是简单提一下。

  到目前为止,咱们看到的一直都是基于一个特定对象的属性的transition。例如咱们将这个方块的x属性的值变到200。

  但有时候咱们须要根据一个变量值的变化来更新可视化图形中的许多部分。

  咱们能够经过.tween和.interpolate方法来实现。全部的方法在d3的文档中都有说明。

  还有一个是d3 timer的使用,它容许重复调用一个函数,也能够用来建立动画。

 

  我一直但愿能够经过相对简单的代码和技术来实现你想要的东西,特别是对于链接多个transitions,以及在恰当的时刻添加和删除对象。要建立更强大的效果,还有很长的路要走!

原文地址:animations and transitions

相关文章
相关标签/搜索