使用viewBox添加动画

发布于 彦子

我最近在CodePen上发表了一个DEMO来庆祝新的一年,然后得到了很多积极的反馈,关于我如何使用viewBox来作为我的场景中的camera的。

所以我决定来写一篇文章来介绍一下这个动画是如何创作的~

Happy New Year !

打开礼物之后,点击小场景来放大

什么是viewBox

我不会讲得很详细,网上有很多关于viewBox文章都讲得很好。

简单来说,viewBox是SVG中可见的内容区域。任何超出viewBox的内容都是不可见的,依赖于你的SVG的尺寸和比例。

你可以在<svg>标签中定义这样四个值:

<svg viewBox="min-x min-y width height">

更多内容,可以查看Sara Soueidan的理解SVG坐标系和变换:视窗,viewBox和preserveAspectRatio一文。也可以查看w3cplus上SVG板块的文章学习~

好了开始吧!

首先我创建一个简单的HTML文档,包含一个SVG。

Freepik设计的图(我进行了简化)

SVG中定义的viewBox是实际上显示在我们屏幕上的尺寸。也就是说我们的SVG的元素都是的viewBox中显示的。

试着调整窗口的大小(或编辑面板的大小),你会看到SVG是如何缩放的~

更新viewBox

在这个DEMO中,我绘制了一个红色的半透明矩形来展示我们将要给viewBox应用的新值。将鼠标移动到矩形上,来更新viewBox,看看SVG如何重新绘制。

很神奇,对吧?

我们的SVG没有变,所有在viewBox外面的元素依旧在页面上,但是依旧看不到。

因为SVG的尺寸和比例,你可能只看到场景水平轴上的一些部分(至少在这支嵌入的pen中)

如果你不理解我是如何给viewBox应用新值的,点击javascript面板

通过viewBox添加动画

现在我们知道要如何更新viewBox的值了,但是还完全算不上动画。

遗憾的是我们目前还不能通过CSS来给viewBox添加动画,我们需要一些javascript。

我经常使用[TweenMax](//greensock.com/来创建动画,因为这是我目前自己觉得最好用的这方面的javascript插件。

这是一个简单的动画实例,viewBox从原始尺寸变为矩形。

我们来看看如何在TweenMax中设置动画:

TweenMax.to(
  svg, //我们的SVG
  2, //动画的持续时间
  {
    attr:{//在新的frame中更新属性的值
      viewBox: "220 110 370 350" //指定动画的最终值
    },
    repeat: -1, //不断重复我们的动画
    yoyo: true, //动画会像悠悠球一样来回播放
  }
);

你可以看到,让viewBox从一帧到另一帧的补间其实非常容易。只需要这几行代码,你就可以创建很多很多的可能性。

使用viewBox作为camera

我们来看看我的那个Happy New Year pen,这是我如何使用viewBox来作为camera来对场景进行缩放。

我在SVG创建了可点击的区域,但是只有在hover的时候可见。我们使用它来触发点击小场景的事件。

//Select all my areas
var overlays = svg.querySelectorAll("#overlays > g");
//在每个区域添加事件监听
for (var i = 0; i < overlays.length; i++) {
  overlays[i].addEventListener("click", zoomViewBox);
}

//这个变量用来检查viewBox是否已经放大了
var zoom = false;

//我创建了包含每个区域的值的对象
//每个key都对应一个区域的id
 var viewBoxes = {
    "overHouses": {
      x: 43,
      y: 290,
      width: 130,
      height: 67
    },
    "overSnowmen": {
      x: 250,
    [...]
  };

当用户点击了任何区域,我们可以调用zoomViewBox()函数:

function zoomViewBox(e) {
  //如果用户在viewBox已经放大的时候点击区域
  //或者,最初的动画还没有结束,先暂停函数的调用
  if (zoom || !animationOn) {
    return;
  }
  //To prevent other unwanted events
  e.stopPropagation();
  //Each area has a unique ID which
  var id = this.getAttribute("id");
 
  //这块代码和前面的实例是一样的,但是现在值不是静态的
  //我把它们从viewBox对象拿出来,赋一块包含id的可点击区域
  TweenMax.to(svg, 2, {
    attr:{
      viewBox: viewBoxes[id].x + " " + viewBoxes[id].y + " " + viewBoxes[id].width + " " + viewBoxes[id].height
    },
    ease: Power3.easeOut
  });
 
  //现在SVG已经放大了
  zoom = true;  
}

允许用户放大,我检查了SVG上的每一次点击,如果SVG是放大状态的,那我们就调用unZoom()函数。

//监听SVG上的每一次点击
svg.addEventListener("click", unZoom);
function unZoom() {
    zoom = false;//将值设置为false
    //这还是同一个动画,只是现在不是初始值
    TweenMax.to(svg, 2, {attr:{viewBox:"0 0 1600 900"}, ease: Power3.easeOut});
}

以上是整个JavaScript的一个大概。如果你对这块感兴趣的话,我鼓励你去看看完整的代码。

案例

这里的这个实例,默认的viewBox后面隐藏了三个场景,在用户做出选择时候是可见的。

(点击RERUN来重置pen)

另一个来自David Bachmann Johannesson的实例你可能已经在Codepen上看过了。

感谢

希望这篇文章在viewBox这块对你有一点帮助。

有任何问题欢迎戳我的Twitter或codepen

本文根据@Louis Hoebregts的《Animating the viewBox》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处://codepen.io/Mamboleoo/post/animating-the-viewbox