CSS 学习笔记 - 动画及其性能

432 阅读4分钟

CSS 动画为页面创造了丰富多彩的效果和无限的可能性,并极大地提高了用户的观感体验,本文梳理一下用 CSS 实现动画效果的方法。

大体上讲,CSS 有两种可以实现动画的方法,可概括为 transition 法和 animation 法。

但是在介绍这两种方法之前,首先要了解一个非常重要的属性——transform

一、transform

transform属性顾名思义,即对元素进行“形变”,可以实现元素的平移、缩放、旋转、倾斜等效果 (【注】transform对 inline 元素无效)。

transform常用的值有:

  • translate() 平移
  • scale() 缩放
  • rotate() 旋转
  • skew() 倾斜

translate()可以对元素进行平移。

transform: translateX(20px) /* 向右平移20px */
transform: translateY(50%) /* 向下平移自身高度的50% */
transform: translate(20px, 20px) /* 同时向右、下平移20px */

scale()对元素进行缩放。

transform: sacleX(1.2) /* 宽放大为原来的1.2倍 */
transform: sacle(1.2) /* 宽、高均放大为原来的1.2倍 */

rotate()对元素进行旋转。

transform: rotate(45deg) /* 顺时针旋转45度 */

skew()使元素发生倾斜。

transform: skew(45deg) /* 倾斜45度 */

了解了 transform 的基本用法,就可以为元素添加动画了。

二、用 transition 实现动画

transition 实际上是实现一种样式向另一种样式的过渡,因此在用 transition 实现动画前,首先要有该元素的两种不同的状态(比如 hover 和非 hover)。

在非 hover 的状态下加上 transition 属性,这样当我们鼠标悬停在元素上时,transition 会自动填充由非 hover 态样式向 hover 态样式转变的过渡动画。

例如,要实现 hover 的时候,div 旋转45度:

div{
  height: 50px;
  width: 50px;
  border: 1px solid red;
  transition: transform 1s linear;
}
div:hover{
  transform: rotate(45deg);
}

当 hover 的时候,div由原来的默认样式变为顺时针旋转45度,transition 使得这两种状态的转变用1s的时间去完成,期间自动补充了过渡动画,即形成了动态的视觉效果。

transition 是一个简写属性,它的值包括属性名、持续时间、过渡方式、延迟时间,通常来说属性名和持续时间是必须的。

对属性名和过渡方式的说明:

  • 属性名用以指定需要过渡的属性,all 表示所有属性
  • 过渡方式是指,完成这一过渡所遵从的【进度-时间】函数关系模型,诸如匀速变化、先快后慢、先慢后快等等,CSS 提供了多种函数模型,如 linear、ease、ease-in、ease-out...

需要注意的是,并非所有的属性都支持过渡,像display: none-->display: block的变化就不支持,类似的需求可以用visibility: hidden-->visibility: visible代替。

三、用 animation 实现动画

transition 动画有一定的局限性,它只能指定两种变化状态(关键帧),即开始和结束,其中间的过程都由数学函数控制,而 animation 可以指定多个关键帧,相较而言更加灵活。

使用 animation 前首先要声明好各个关键帧,这里用到的是@keyframes语法,其写法如下

@keyframes xxx{
    0% {样式一}
    10% {样式二}
    n% {样式三}
    ...
    100%{样式四}
}

@keyframes xxx{
    from {样式一}
    to {样式二}
}

其中的xxx表示为该动画设定的名字。

声明好关键帧后,就可以用 animation 属性为元素应用这个动画了

div{
    animation: xxx 1s infinite alternate-reverse; 
}

animation 的值包括动画名、时长、过渡方式、延迟、次数、方向、填充模式、是否暂停等,具体用法参见MDN

四、动画的性能

实现同一种动画效果,可能会有诸多方法,比如要做一个将 div 从左往右移动的动画,可以先令 div 相对定位,然后用 JS 不断改变其 left 的值:

var n = 1
var id = setInterval(() => {
  if (n <= 100) {
    demo.style.left = n + 'px'
    n = n + 1
  }else{
    clearInterval(id)
  }
}, 1000 / 30)

也可以用前文所述 transform + transition 法去做,表面上看效果一样,其实深究起来前者的性能会差很多,要讨论这个问题还要先从浏览器的渲染原理入手。

浏览器在渲染一个页面的时候,有以下几个步骤:

  1. 由 HTML 构建 HTML 树(DOM)
  2. 由 CSS 构建 CSS 树(CSSOM)
  3. 将以上两棵树合并成一棵渲染树(Render Tree)
  4. 进行 Layout 布局(依据文档流、盒模型确定大小、位置)
  5. 进行 Paint 绘制(内容、色彩)
  6. 进行 Compose 合成(依层叠顺序呈现出来)

实际在更新页面的时候,某些情况下可能会省略掉 Layout 过程,也可能会同时省略 Layout 和 Paint,直接进入最后的 Compose,这样就会比走完整个流程的情况性能更好。

以上第二种用 transform 的方法,就跳过了 Layout 和 Paint,直接进行 Compose,因此在性能上要好很多。

然而遗憾的是,并没有确切的规律去帮助我们判别哪些属性会跳过某个过程,只能逐个去试验,但是好在已经有人试验了所有属性,并且总结出了每个属性所触发的渲染流程,参见CSS Triggers