复杂网页动画的实现

编者记:在最新一次的凡泰极客官网改版中,首页中的动画效果是由前端工程师陈林主导开发与实现的,在网站上线后也得到了众多同事与用户的好评。因此我们邀请了他向我们做一次《复杂网页动画的实现》的内部分享,以下是分享正文。


凡泰极客官网截图
交互效果截图

为了让网页显得生动有趣,设计师往往会设计一些或简单或复杂的小动画,开发人员需要考虑这些动画该怎么实现,不仅要完整还原设计稿的效果,还要保证网页的性能、动画的流畅性以及对不同浏览器的兼容等等。

做过网站前端开发的人都知道,CSS 里面的 transition 和 animation 可以实现动画过渡效果,然而实际开发中遇到的情况往往比较复杂,仅仅用这两个属性还不能解决问题。本文旨在分享一些比较复杂的网页动画(如连续执行的动画队列、非标准曲线动画等)的实现方法。

复杂动画

首先我们要搞清楚什么样的动画才算复杂动画,先看下面两个例子:

图 1
图 2

上面图 1 为直线运动,在代码里面,只需提前定义好元素的起始位置和终点位置即可,这种动画比较简单和常见。而图 2 中的动画是曲线动画,不仅是曲线而且是不规则曲线,无法简单的在代码中通过定位来实现。曲线动画在设计稿里面很常见,要知道,设计师在实现某个创意的时候一般不会考虑程序员怎么实现的,所以他们设计的动画往往没什么规律可循,这种动画我们称之为复杂动画。

再来看看下面的例子:

图3

在这个例子里面,同时有直线动画和曲线动画,元素在不同阶段运动方式不同,而且需要保证动画效果在同一个元素上是连续执行的,这就涉及到动画队列的管理。

实现方式

简单粗暴的实现动画当然是用 gif 图片或者网页视频。对于 gif 图片,这是一种比较古老的做网页动画的方式,至今仍然比较常见,它的缺点是颜色失真较多、比较耗费浏览器性能、无法实现半透明效果等。

至于用网页视频来显示动画,也不失为好方案,毕竟现在有 webm 等新技术,浏览器对他们的支持也做的相当好了。但是,用视频制作网页动画也有缺点,首先你需要有做视频动画的专业人员来支持你的工作,其次是视频动画无法很好的实现一些类似点击之类的交互操作。

对于以上这些非代码实现的动画方式,这里不做过多讨论,我们谈谈怎么用纯前端代码的方式实现复杂动画。

动画叠加法

动画叠加法仅适用于可分解为简单动画的元素变换,如贝塞尔曲线运动,看下面的例子:

图 4:抛物线运动

这是一条类似物理中的抛物线路径,我们把水平方向的运动想象成 x 轴,垂直方向的运动想象成 y 轴,那么上面的动画可以分解成下面两个动画:

图 5:x 轴
图 6:y 轴

可以看到这两个都属于简单动画,两个方向的 animation-timing-function 值不同,即运动速度快慢不同,合成之后就能实现上面的抛物线动画。具体实现方式可以参考张鑫旭的博客文章《这回试试使用CSS实现抛物线运动效果》

CSS3 中的 Motion Path

动画叠加法对于那些无法分解或者分解起来比较麻烦的运动路径就有些无力了,这时候就需要用到 CSS3 中的 motion path 的概念。只需要给元素提前设置一个 offset-path ,然后用 animation 控制元素的运动位置,这样,元素就会顺着设置好的路径运动。offset-path 值可以是 SVG 中的 path 形状值,也可以是 CSS 预置的一些形状函数。

.scissorHalf {
  offset-path: path('M900,190  L993,245 V201  A11,11 0 0,1 1004,190  H1075  A11,11 0 0,1 1086,201  V300  L1294,423 H1216  A11,11 0 0,0 1205,434  V789  A11,11 0 0,1 1194,800  H606  A11,11 0 0,1 595,789  V434  A11,11 0 0,0 584,423  H506 L900,190');
  animation: followpath 4s linear infinite;
}

@keyframes followpath {
   to {
     motion-offset: 100%;
     offset-distance: 100%;
   }
}

绘制 SVG 路径可以使用 Illustrator 、Photoshop 或者其他矢量图制作工具,这里推荐一个免费的在线图片编辑器 Photopea,使用其中的钢笔工具即可绘制并导出 SVG 路径。

注意:由于 motion path 是一个比较新的概念,目前(2021年9月26日)Safari 浏览器还不支持该属性,用的时候需要考虑到这点。

SVG 使用 SMIL

SMIL,即同步多媒体集成语言,是由 W3C 标准协会为了用 XML 描述多媒体而建议的一种标记语言,它可以让 SVG 实现动画效果。

SVG 中的动画实现主要借助 <animate>、<animateTranform>、<animateMotion> 等标签,具体语法请参考 MDN 文档

<svg width="300" height="100">
  <title>SVG SMIL Animate with transform</title>
  <rect x="0" y="0" width="300" height="100" stroke="black" stroke-width="1" />
  <rect x="0" y="50" width="15" height="34" fill="blue" stroke="black" stroke-width="1">
    <animateTransform
       attributeName="transform"
       begin="0s"
       dur="20s"
       type="rotate"
       from="0 60 60"
       to="360 100 60"
       repeatCount="indefinite"
	/>
  </rect>
</svg>

JavaScript 工具

上面提到了一些实现 CSS 曲线动画的方式,他们都能实现单个元素的单个动画,但是,对于单个元素的多个动画却不太管用。为了更好的控制动画流程,最终还是需要使用 JavaScript 工具。目前可以找到的使用的比较多的动画制作工具有 GSAP、Anime.js、Matter.js、Three.js ,下面对这张表格展示了这几个工具的对比。

JS 工具 支持队列 支持时间线 支持物理模拟 支持 3D 模型 优点 缺点
GSAP ✔️ ✔️ 生态完善,功能齐全,API 容易上手 文件体积较大,有一定的使用限制
Anime.js ✔️ ✔️ 轻量,功能齐全,完全开源 由于比较轻量,可扩展性上有一定牺牲
Matter.js ✔️ 可实现2D物理效果模拟,如重力、碰撞等等 需要具备一定的物理基础知识,仅适用于特定动画场景
Three.js ✔️ 可渲染复杂的3D效果 上手难度高,需要结合数学知识和纯代码逻辑的方式来实现动画