探索 css 动画的奥秘

动画的意义

动画是什么

动画可以吸引注意力,也可以更有效的传达信息。精美、微妙、适当的动画能提供一种更强大的交流方式,能够让用户更直观“赏其美,懂其意”。
随着现代游览器的不断进步及优化,能更好地支持动画,在许多方面,它和页面中使用的字体及布局一样重要。

在页面中,动画有两种作用:一承接上下文,二提升用户使用体验。例如我们常见的大转盘抽奖动画,转动动画不仅体现了通过随机抽取,奖品出现,而且提升了用户的效果,增加了乐趣。

在 css 中,动画一般通过 transition 和 animation 来实现,那么两者有何区别呢?在接下来的内容中将详细介绍两者的属性及适用场景。

Transition

transition 定义

transition 是控制游览器中动画的一种方式,即从一种状态过渡到另一种状态。

当在元素上使用过度时,游览器会自动计算状态之间的变化。并且可以使用 duration 和 timing-function 使其变慢、变快,控制变化率,使用 delay 进行延迟动画。

transition 复杂动画实例(本文实例均可通过下方 codepen 链接演示):
实例1
https://codepen.io/suez/pen/XJGOyL

transition 属性速记

transition 具有下列四个属性:
transition-property:制定 css 属性,如 width、height、all 等
transition-duration:效果所需要的时间
transition-delay:告诉浏览器在过渡之前要等待多久
transition-timing-function:效果的转速曲线,如 linear,ease-in,ease-out,ease-in-out、cubic-bezier。cubic-bezier可以创建自定义的速率曲线,善用它可以帮助开发者创建更加精美的动画效果。

transition 适用场景

transition 一般用来修改大小、位置、边框、背景等情况下使用,

不仅可以在单个元素上使用过渡效果,当然也可以在多个元素进行组合过渡、或者在单个元素增加多个过渡效果。

具体实例如下(本文实例均可通过下方 codepen 链接演示):
https://img12.360buyimg.com/imagetools/jfs/t1/136649/16/1699/41510/5edce64fE2fde24d7/c3b8b70764654f93.png
https://codepen.io/marphydemon/pen/qBbdxwa

animation

animation 介绍

动画和过渡相似。两者均采用 css 属性的形式,都存在延迟(delay)、转速曲线(timing-function)、完成时间(duration)。过渡是从 A 状态平滑过渡到 B 状态。但是动画可以实现更为复杂的动画,例如从 A-B-C 三种状态,过渡只能从 A-C,而动画允许开发者实现 B 的样式。

动画一般通过鼠标悬停或者 JavaScript 添加或删除类等动作触发,并通过 keyframes 创建每一个关键帧,使浏览器在关键帧之间自动计算并执行动画。keyframes 单独定义的好处之一是,它允许我们创建可以多次重用的动画。

当你需要逐帧编写动画的时候,它将是你的首选。

animation 属性

animation 相比于 transition 具有以下额外的属性:
一个 animation 可以写成以下简写:

animation: change-background 4s linear infinite;
作为单独的属性写成:

animation-name: change-background; //动画 keyframes 集合的名称
animation-iteration-count: infinite; //动画播放次数,infinite(无限)
animation-direction: reverse; //是否循环交替反向播放动画
animation-fill-mode: forwards: 当动画不播放或者完成时的样式
animation-play-state: 控制动画运行或暂停

animation 实例演示

通过一个实例能更有效的展示动画如何运行。

实例2

通过实例可以清晰的看出,当动画从 0 开始时,背景开始从红色开始,随着动画的进行,浏览器会创建从每个背景色到下一个背景色的中间帧,创造平滑的动画。

需要注意的一点是 animation-timing-function 可以应用于动画的每个关键帧。
例如:

@keyframes my-animation {
    0% {
        ...
        animation-timing-function: linear;
    }
    50% {
        ...
        animation-timing-function: ease-out;
    }
}

在这种情况下,动画的前半部分将是线性的,而后半部分将使用 ease-out 定时功能。

接下来,我们将更细致地介绍关键帧。简单的动画中,相信大家经常使用 from and to。

@keyframes name {
  from {
    ...;
  }
  to {
    ...;
  }
}

这是编写 0%-100%的一种替代方式,更易理解。而在其他动画中可能有注意到同一行使用多个百分比值,这是使动画保持特定状态的一种方法。例如叠蛋糕项目中:
叠蛋糕
蛋糕动画演示:https://codepen.io/marphydemon/pen/VweLdpa

animation 适用场景

css 动画是未来的发展趋势,且它的适用场景很广泛,你可以用它尝试着去编写你能想象到的所有动画。

目前为止介绍了 animation 的属性以及如何依赖 keyframes 关键帧创建动画。接下来聊聊 css 动画开发思路。

动画开发思路

刚开始对于动画大多数人应该是好奇想尝试,却因为不熟悉而缺乏练习,在需求中遇到动画,也难以直击要点,不知从何开始。动画其实并不复杂,遇到动画时,应该试着逐步去分解动画。将一个完整的动画逐步分解为一个个的小动画,拆分出主动画和从属动画,分别开发完成后将其组合起来形成一个完整的动效。

此处以前文提到的叠蛋糕动画为例。
叠蛋糕
上图是一个完整的叠蛋糕演示,接下来从细节方面分析从无到有如何开发这样一个动画。先奉上一张流程图。
叠蛋糕升级动效流程图
在拿到需求的时候,需要根据演示 demo 制作如下流程图(大佬当然例外),将其拆分为两类动画:

  1. 主动画(蓝色框标出)
  2. 从属动画

主动画是整个升级动画中最主要的模块,抓住重点,升级用户最关注的就是最新层蛋糕的增加,开发后期需要重点优化调整的也是主动画。而从属动画是由于主动画所引起的附带品,相对关注度较低,一般开发顺序为先主后副,叠蛋糕中,从属动画较多,开发完主动画后,可适当根据主动画进行从属动画的调整。

分清主动画及从属动画之后,进行主动画剖析:
蛋糕从左上角旋转飞入,然后叠加到蛋糕塔上,由于新增蛋糕的飞入,造成蛋糕塔被挤压晃动。这里可以想象当弹簧从弯曲拉开状态到恢复原状的过程中,引起的弹窗挤压又弹起。
在不同的层数分为三种情况进行讨论:

  1. 当蛋糕层数小于 4 层时,新增蛋糕叠加,蛋糕塔晃动,蛋糕塔不需要下移
  2. 蛋糕层数等于 4 层时,新增蛋糕叠加,蛋糕塔晃动的同时蛋糕塔下移最下层一半效果,展示 3.5 层
  3. 蛋糕层数大于 4 层时,新增蛋糕叠加,蛋糕塔晃动的同时蛋糕塔下移一层效果,仍然展示 3.5 层

通过帧动画控制蛋糕塔的晃动,在大于等于 4 层时,增加固定 css 保持 3.5 层显示。在动画完成之后对各层样式进行复位重置,弹出升级弹窗。
从属动画相较于主动画较为简单,在动画整个过程中,单独进行控制,为了效果体验尽量配合主动画进行开发,例如小精灵,可以看做蛋糕掉下来的过程中有所恐惧进行后退等,使动画尽量看上去流畅,不突兀。从属动画在这里就不展开讨论,想尝试的朋友可以自己动手试试。

接下来聊聊动画开发过程中比较容易忽略的点:

  1. 动效的开始往往跟接口有着巨大的联系,动画的开始意味着我们的接口已经请求成功,此次操作不会引起逆向问题。举个例子,比如在用户点击叠蛋糕的时候,接口请求成功,方可开始执行动画效果,如果接口请求失败,走对应的异常逻辑,而非进行动画效果,否则此时蜡烛开始消失,动画开始执行,后台却无新增蛋糕,级数未曾改变,引起前后端差异,极易造成体验差,用户投诉等。
  2. 循环播放动画,应以固定速度,通过设置总动画时间决定播放次数。以东东萌宠为例,在遛狗过程中,5s 内运动距离为两张背景图宽度,不能以固定时间固定距离去控制速度,后期维护工作量大、修改繁琐易出问题。优化过程中修改实现方案,宠物运动维持恒定速度进行动画,需求变更时,只需修改宠物运动的总时间而不用处理其他细枝末节。

css 动画性能优化

在 css 动画开发中,使用 transform,而非直接控制 width、height 等,为什么可以提高动画的性能?
大多数人都知道现代浏览器使用 GPU 渲染网页的部分,尤其是带有动画的网页。例如使用 transform 的动画比使用 left 和 top 的动画更加平滑。如何从 GPU 获得更平滑的动画?很多地方会告诉你使用“transform: translateZ(0)”或“will-change: transform”,那这些又是为什么?

实例分析

如下图,点击按钮将 紫色 元素通过改变 left 及 top 属性使其沿正方形轨迹运动。
实例3
https://codepen.io/marphydemon/pen/RwrPByY

https://img12.360buyimg.com/imagetools/jfs/t1/145364/17/213/78471/5ede10a2E553c4d5c/b8d1fdc8ccb5b929.png

如上图所示,当通过改变 left、top 执行动画时,每一帧动画浏览器都需要进行如下处理:

  1. 计算需要加载的节点样式结果,即样式重计算
  2. 计算每个节点的大小及位置,即重排
  3. 将每个节点填充到图层,即重绘
  4. 组合图层到页面上,即图层重组

在这种情况下,对于每个动画帧,浏览器都必须重新计算元素的形状进行重排,渲染页面新状态的图片,再次发送到 GPU 从而在屏幕上显示。重新绘制的性能非常昂贵,虽然大多数浏览器可以快速重绘,但是动画仍然不流畅。在动画的每个步骤上进行重排和重绘整个页面,对于更加复杂的页面将会更加影响性能体验。

可触发重排的属性

触发重排一般为修改了元素的位置和大小,具体如下:
盒子模型相关属性会触发重布局:

  • width
  • height
  • padding
  • margin
  • display
  • border-width
  • border
  • min-height

定位属性及浮动也会触发重布局:

  • top
  • bottom
  • left
  • right
  • position
  • float
  • clear

改变节点内部文字结构也会触发重布局:

  • text-align
  • overflow-y
  • font-weight
  • overflow
  • font-family
  • line-height
  • vertival-align
  • white-space
  • font-size

可触发重绘的属性

触发重绘为修改了元素的渲染效果,修改时只触发重绘的属性有:

  • color
  • border-style
  • border-radius
  • visibility
  • text-decoration
  • background
  • background-image
  • background-position
  • background-repeat
  • background-size
  • outline-color
  • outline
  • outline-style
  • outline-width
  • box-shadow

为了使动画性能提高,所需要做的就是游览器在动画运行时需要做的工作。而现代浏览器如 chrome 等都对 transform、opacity 采用硬件加速,即生成新的图层,在单独的渲染线程上运行。

例如使用 transform 替换 left、top 如下:
实例4
https://codepen.io/marphydemon/pen/jOWPpdb

声明性地描述动画的开始位置、结束位置、持续时间等、可以提前告诉游览器那些 css 属性将被更新。浏览器发现所有属性都不会导致重排或者重画,所以它可以合成优化:将两个图像绘制成合成层并发给 GPU。例如 transform: translate(0, 0)到 transform: translate(0, 300px),主线程只需要进行一次 tranform: translate(0, 0)到 transform: translate(0, 300px),然后合成线程去 0-300 一次次转换。

如实例仅绘制两个单独的图像(一个用于移动元素,一个用于不包含移动元素的整个页面),然后简单的使图像相对于彼此偏移。这正是 GPU 的亮点:它能够快速合成图像,省略重排和重绘,从而为动画增添了平滑度。

动画为页面提供了更为丰富的用户体验,提升了页面的吸引力。所以在开发动画过程中,应该尽量避免使用会触发重排和重绘的属性,在开发过程中,由于 GPU 的参与,最好使用以下属性:

  • opacity
  • translate
  • rotate
  • scale

这样做的优点是什么?
能够获得更加平滑流畅的动画,而且 transform 动画运行在为图形任务特别优化的线程上运行,且它的运行速度非常快。
动画不再需要绑定到 CPU,即使 CPU 运行非常繁重的任务,但是动画仍将快速运行。

值得注意的是通过 transform 属性可以形成了新的 CompositingLayer 合成层。而合成层的处理依赖硬件加速,然而 GPU 内存有限,所以适度使用。

总结使用 GPU 加速动画优缺点

优点

动画快速流畅,每秒 60 帧。
精心制作的动画可以在单独的线程中工作,并且不会被繁重的 JavaScript 计算所阻止。

缺点

需要额外的重涂以将元素提升为复合层。有时这很慢,比如,当我们进行全层重绘而不是增量重绘时。
绘制的图层必须传输到 GPU。根据这些层的数量和大小,传输也可能非常慢。这可能会导致低端和中端设备上的元素闪烁。
每个合成层都占用额外的内存。内存是移动设备上的宝贵资源。过多使用内存可能会导致浏览器崩溃。

以上是我对于动画的一些小小见解,如有错误,欢迎指正。

猜你喜欢

转载自www.cnblogs.com/MarphyDemon/p/13367263.html