CSS动画技巧及性能

接着前文看《CSS权威指南》,本文整理一些CSS动画的知识点,同时给出一些例子。

预备知识

变形 Transform

在CSS动画中我们经常需要通过变形来达到一些奇妙的效果。使用 transform 首先要明确变形是基于笛卡尔坐标系,也就是通常所说的 x/y/z
坐标系

  • transform 是基于物体自身的坐标系,百分比也是基于自身的大小
  • 移动和旋转的前后顺序很重要,因为物体旋转之后的坐标系也跟着旋转
  • 常用函数(live demo):
    • 平移函数:translateXtranslateYtranslateZtranslatetranslate3d
    • 缩放函数:scaleXscaleYscaleZscalescale3d( number, number, number )
    • 旋转函数:rotateXrotateYrotateZrotaterotate3d( number, number, number, angle)
    • 倾斜函数:skewXskewYskew
    • 视域:perspective( length )
  • 重要属性:transform-origintransform-stylebackface-visibility

贝塞尔曲线

在过渡或者动画效果中,我们可能需要使用贝塞尔曲线 cubic-bezier 来控制动画的步调。我们可以在这里 设置不同的曲线并预览,或者在这里 查看一些常用的曲线

动画

属性

  • animation-name:指定动画的名称
  • animation-duration:定义动画的时长(ms | s)
  • animation-iteration-count:声明动画的迭代次数(number | infinite)
  • animation-direction:设置动画播放的方向(normal | reverse | alternate | alternate-reverse)
  • animation-delay:延迟播放动画
  • animation-timing-function:改变动画的内部时序
  • animation-play-state:设置动画的播放状态(running | paused)
  • animation-fill-mode:动画的填充模式,也就是动画结束之后的状态(normal | forwards | backwards | both),最终的显示效果与 animation-direction 属性有关
    live demo

    JS动画事件

  • animationstart:动画开始
  • animationiteration:每次迭代开始
  • animationend:动画结束

动画延迟效果

给不同对象赋予一定的延迟时间,往往可以形成一些有趣的动画效果。这里我们要区分两种延迟:

  • 浏览器把动画附加到元素上之后等待多久开始第一次迭代:animation-delay: <time>
  • 每次迭代开始前需要等待:重复关键帧
    animation delay
      /*假设一个小球来回滚动,每次在两边时停留800ms再继续滚动,我们可以由以下动画控制*/
      .ball {
        width: 50px;
        height: 50px;
        border-radius: 50%;
        background-color: orange;
        animation: move 2000ms infinite alternate;/*alternate 设置来回滚动*/
      }
      @keyframes move {
        0%,
        40% {
          transform: translateX(0);/*重复关键帧模拟延迟,2000ms × .4 = 800ms*/
        }
        60%,
        100% {
          transform: translateX(250px);/*重复关键帧模拟延迟,2000ms × .4 = 800ms*/
        }
      }
    
    live demo

从中间帧开始执行动画

如果animation-delay的值为负,而且绝对值比animation-duration 小,那么动画将立即开始并且从中间的关键帧开始播放。例如,一个动画时长为 10s,延迟时间为 -4s,那么如果 animation-timing-functionlinear,则将从动画的 40% 开始播放

利用 box-shadow

利用 box-shadow 可以创建为单个元素创建多个 border。例如,我们可以利用这个属性为一个 loading 动画添加多个小的圆圈。

利用伪元素

我们注意到利用 box-shadow 属性,所有“子元素”是绑定在“父元素”上且同时变化的。如果我们不想引入多余的 DOM 元素,同时又想“子元素”不与“父元素”同时进行变换,我们可以采用::after::before来创建。

利用clip-path

来看一些有趣的例子吧

利用上述知识写的一些简单的动画(点击查看源码)


See the Pen
Spinner-13
by Mars_i (@susiechang-the-styleful)
on CodePen.


See the Pen
Spinner-12
by Mars_i (@susiechang-the-styleful)
on CodePen.


See the Pen
Spinner-02
by Mars_i (@susiechang-the-styleful)
on CodePen.


See the Pen
Spinner-06
by Mars_i (@susiechang-the-styleful)
on CodePen.


See the Pen
Spinner-09
by Mars_i (@susiechang-the-styleful)
on CodePen.

动画性能

讲完了一些基础知识,让我们进一步聊聊动画渲染的性能。

浏览器底层原理

浏览器的工作原理,请看 How Browsers Work: Behind the scenes of modern web browsers,写的很详尽,值得一读。

浏览器处理的过程很简单:计算元素的样式(重新计算样式),生成每个元素的几何形状和位置(布局),绘制图层中的每个像素(初始化绘图并且进行绘图)并且将图层绘制到屏幕上(图层的合成)

浏览器内部

现代浏览器通常有两个主要的线程:① main thread;② compositor thread

  • 主线程的主要职责是(维持一个RenderLayers):
    • 运行脚本
    • 计算 HTML 元素的 CSS 属性
    • 页面布局
    • 把 DOM 元素绘制到一个或者多个位图上
    • 把合成的位图交给compositor thread
  • 合成线程的主要职责是(维持一个 GraphicsLayers):
    • 通过 GPU 把位图绘制到屏幕上
    • 请求主线程更新呈现在屏幕上或者即将呈现在屏幕上的位图
    • 当进行滚动操作时,弄清楚哪部分即将呈现在屏幕上
    • 当进行滚动的时候,移动相应部分的页面

合成线程会尽可能响应用户的鼠标键盘等消息事件。可能发生的情况是,当页面滚动需要更新视图时,合成线程以每帧(60fps)请求更新位图(刷新屏幕),但主线程上可能正进行复杂的脚本运算或者HTML元素绘制,导致合成线程无法及时拿到位图,这时合成线程会绘制空白。

GPU

GPU 能够很快地处理将位图绘制到屏幕上的操作,还能很快处理将同一位图通过变换再次绘制的操作,但是对于从内存中加载位图的处理比较慢。比较一个过渡效果使用 height 和 transform 的两个流程:

composited layer 创建的条件

  • 使用视频加速解码的 video 元素
  • flash、使用3D (WebGL) context 的 canvas 元素
  • CSS transform
  • CSS filter
  • CSS opacity

高性能的 CSS动画

减少消耗成本

为什么有些动画播放流畅,有些则显得很卡顿呢?从上面的知识中,我们或许会得到一些启发:现代浏览器在完成 position(位置), scale(比例缩放), rotation(旋转) 和 opacity(透明度)这四种属性的动画时,消耗成本较低。

如果我们使用 height 进行变换,浏览器可能在每帧需要请求主线程重新布局、绘制和更新位图,再将位图从 CPU 传给 GPU,这个过程可能导致卡顿,也就是我们丢失了某些关键帧。

但是使用 height 的性能就一定比 transform 的性能差?不一定,可能的情况是:① 绘制的位图很小;② height 更新不会影响其他元素的布局等情况;③ 元素是脱离正常流的;

我们在使用 width、display、font-size、box-shadow、color 等可能引起重排或者重绘的属性时,如果你不能清楚的知道自己在做什么,我们还是建议使用推荐的操作避免糟糕的情况出现。

JS动画和CSS动画的优缺点

  • 优点:JavaScript中的动画可以为你提供更多的控制,比如开始,暂停,回放,中断和取消等,还可以进行设计复杂的动画效果。缺点:主线程上可能进行复杂的运算,增大掉帧的风险(我们也可以放在 Web Worker 中进行)。
  • 优点:浏览器会对CSS动画进行优化,如果有需要,它会创建图层,并且可以在主进程之外完成一些操作。缺点就是CSS动画相对于Javascript动画而言,缺乏表现力,对动画的设计来说更加复杂。

参考博客及推荐阅读


  转载请注明: Blog CSS动画技巧及性能

 上一篇
【翻译】React Hooks 是什么 【翻译】React Hooks 是什么
原文链接What are React Hooks?文章很长,我尽量采取意译,一些不重要的内容没有翻译。文章中涉及到这个作者的很多其他文章,都非常值得一看而且通俗易懂,建议大家可以直接看作者的英文博客。这篇翻译只是我自己一个学习记录,请勿转
2019-08-18
下一篇 
使用Web Audio API实现简单的音频可视化 使用Web Audio API实现简单的音频可视化
前言之前刚好看到Web Audio API方面的内容,因此用了相关api做了个音频可视化的页面。实现: 音频播放/暂停 音频声量控制 音频立体声控制 音频频率可视化 音频切换 预备知识Web Audio API中一个关键的对象就是音频上
2019-06-09
  目录