前端动效方案

本文中所有外链打不开时请先尝试魔法上网

基于样式的动画

css3 补帧动画

transition 过渡动画

.box {
  border: 1px solid black;
  width: 100px;
  height: 100px;
  background-color: #0000ff;
  transition: width 2s, height 2s, background-color 2s, transform 2s;
}

.box:hover {
  background-color: #ffcccc;
  width: 200px;
  height: 200px;
  transform: rotate(180deg);
}

场景:常与 :hover, :active等伪类使用,实现相应等动画效果。

animation 关键帧动画

.bounce1 {
  left: -40px;
  animation: bouncy1 1.5s infinite;
}
.bounce2 {
  left: 0;
  animation: bouncy2 1.5s infinite;
}
.bounce3 {
  left: 40px;
  animation: bouncy3 1.5s infinite;
}
@keyframes bouncy1 {
  0% {
    transform: translate(0px, 0px) rotate(0deg);
  }
  50% {
    transform: translate(0px, 0px) rotate(180deg);
  }
  100% {
    transform: translate(40px, 0px) rotate(-180deg);
  }
}

场景:比如 loading 展示。

优点:

  1. 无需每一帧都被记录,通过关键帧设置,方便开发;
  2. 实现简单,通常 UI 可以直接给到 css 文件,前端只需要导入即可【移动端注意屏幕适配】。

缺点:

  1. css 没法动画交互,无法得知当前动画执行阶段;
  2. transition: 需要触发,无法自动播放;
  3. animation 兼容性需要加前缀,导致代码量成倍增长;
  4. 对于复杂动画的实现,导入的 css 文件过大,影响页面的渲染树生成,从而阻塞渲染。比如实现一个摇钱树的效果,css 文件达到百 kb,就要采取一些必要的压缩手段,缩减文件大小。

JS 逐帧动画

JS 动画的原理是通过 setTimeout 或 requestAnimationFrame 方法绘制动画帧,从而动态地改变 网页中图形的显示属性(如 DOM 样式,canvas 位图数据,SVG 对象属性等),进而达到动画的目的。

示例: js 实现一个正方形从左到右的移动动画

  1. setTimeout 实现
    const element2 = document.getElementById("raf2");
    const btn2 = document.getElementById("btn2");
    let i = 0;
    let timerId;
    function move() {
      element2.style.marginLeft = i + "px";
      timerId = setTimeout(move, 0);
      i++;
      if (i > 200) {
        clearTimeout(timerId);
      }
    }
    btn2.addEventListener("click", function () {
      move();
    });
  2. requestAnimationFrame 实现
    const element = document.getElementById("raf");
    const btn1 = document.getElementById("btn1");
    let r = 0;
    let rafId;
    function step() {
      element1.style.marginLeft = r + "px";
      rafId = window.requestAnimationFrame(step);
      r++;
      if (r > 200) {
        // 在两秒后停止动画
        cancelAnimationFrame(rafId);
      }
    }
    btn1.addEventListener("click", function () {
      step();
    });
  3. 区别
    从 rAF 的执行时机,可以看出 setTimeout 的执行时机与 rAF 的不同。
    取 16ms 由于浏览器对最短间隔时间 4ms 的限制可以看到 seTimeout 执行了 4 次,导致此时设置的 marginLeft 和上一次渲染前 marginLeft 的差值要大于 1px。
    如果 js 执行的时间过长,导致在本该绘制一帧的时候,没有绘制,延迟到下一帧的执行绘制的时候,就会造成动画的卡顿。
  4. 总结
    1. setTimeout 时间不准确,因为他的执行取决于主线程执行的时间。
    2. 如果计时器频率高于浏览器刷新的频率,即使代码执行了,浏览器没有刷新,也是没有显示的,出现掉帧情况,不流畅。

    而 rAF 解决了 setTimeout 动画带来的问题:
    1. 浏览器刷新屏幕时自动执行,无需设置时间间隔 和 setTimeout 一样是 n 毫秒之后再执行,但这个 n 毫秒,自动设置成浏览器刷新频率,浏览器刷新一次,执行一次,不需要手动设置;浏览器不刷新,就不执行,没有排队掉帧的情况。
    2. 高频函数节流 对于 resize、scroll 高频触发事件来说,使用 requestAnimationFrame 可以保证在每个绘制区间内,函数只被执行一次,节省函数执行的开销。

    如果使用 setTimeout、setInterval 可能会在浏览器刷新间隔中有无用的回调函数调用,浪费资源。

如何优化基于样式的动画性能?

  1. 提升每一帧的性能
    • 避免频繁的重排
    • 避免大面积的重绘
    • 优化 js 的性能
  2. fps 稳定,避免掉帧,跳帧的情况
    • 不在连续动画中,添加高耗能的操作
    • 如果无法避免,看可以在动画的开头或者结尾进行操作
  3. 开启 GPU 加速

基于图片资源的动画

GIF

gif

优点:支持性比较好,主流浏览器全支持。

缺点:支持颜色少、不支持 alpha 通道(透明度只能取 0 和 1 两个值)

序列帧/帧动画1

设计提供每帧的静态文件,通过 css 的animation-timing-function: steps()逐帧切换图片。

frame

示例:CSS3——animation中的属性--steps - 博客园

缺点:不适合很大尺寸对加载时间要求高的动效,比如直播打赏的礼物动画

APNG

该方案提出就是为了解决 Gif 的问题(颜色、透明度)。APNG 是普通 png 图片的升级版,它的后缀依然是.png,包含动态的情况下体积会比普通 png 大出数倍,可以做到无损的情况展示动态。

APNG 是 Mozilla 在 2008 年发布的图片格式,本质上是在 PNG 的基础上加上一个扩展,而且非常简单即可实现。因此能够完全支持 RGBA。规范可以参见 APNG Specification

虽然这个规范没有加入 PNG 开发组,但是很多浏览器已经支持了 APNG。 最主推的是 Apple 的 Safari(OS X 10.10 以后的 Safari,以及 iOS 8 以后的 Safari 和内置 WebView),已经完全支持。Firefox 亲儿子当然一直是支持的。Chrome 桌面端已经从 Chrome 59 开始支持,现在就差 Edge 了。具体支持程度参见浏览器兼容性。

APNG 的优势,在于时间比较长,各种动图制作工具,优化工具都有相应的项目来支持。而且在 iOS 上的 WebView 里面是除 GIF 外,唯一官方支持的动图格式,因此如果做移动端开发需要 WebView 页引入动图,APNG 还是必不可少的。

当然,APNG 终究是在 PNG 的基础上扩展,并没有引入特别出色的压缩算法,而且遗憾的是,短期内 APNG 还没有引入到 Chrome,也就意味着 Android 平台的 WebView 也没有原生支持,因此,移动开发又会面临两端兼容性问题。

WebP

WebP 是 Google 在 2010 年发布的图片格式,完全开源,使用了 VP8(就是 WebM 视频所用到的解码器)作为帧压缩编码器,而且在 Chrome,Android 上得到了原生的支持,具体规范参见: WebP

同样的支持 RGBA,而且静态 WebP 的压缩率比起同质量 PNG 平均要高上 20%左右。现在各大 App 厂商已经有开始迁移 WebP。除了静态的 WebP,还有动态 WebP 格式 (Animated WebP) 支持,不过动态 WebP 需要 libwebp 0.4 以后才正式支持,并需要 mux 和 demux 模块,如果自行编译需要注意。

Google 官方提供了 libwebp 这个解码库在各个平台的二进制版本和 Makefile,并且可以定制开启的功能。不过由于不像 APNG 那样基于 PNG 扩展,相关的工具很欠缺,基本全靠 WebP Project 提供的工具。

SVG Animation

以上都是位图,图片的尺寸越大,所占控件越大。

本节介绍的是可动 SVG,本质是一段代码,体积小的同时不在乎分辨率。可以看这个文章了解:

超级强大的 SVG SMIL animation 动画详解 « 张鑫旭

生成工具介绍:

SVGator: Free SVG Animation Creator Online

SVGA

pic

官网:SVGA

SVGA 是一种特殊格式,和 SVG Animation 不一样,也不是它的缩写。该工具是 YY 团队开发,专门解决直播中礼物的动画问题。

优点:可以穿插帧,与位图混合使用;作为静态文件,不需要序列化后打包进项目,更新友好。

Lottie

logo

官网:Lottie

国外用的非常多,AirBnb 出品。图片资源和动作数据分离。可以在 AE 上添加插件 BodyMovin,直接导出 json 传给 lottie,使用简单。

参考资料

Footnotes

  1. 两种方式实现帧动画 - 知乎