CSS 动画

CSS 动画

CSS动画的实现原理和性能分析

实现原理

  1. 渲染过程:在浏览器中,动画的渲染过程大致分为样式计算、布局、绘制和合成四个阶段。
  2. 关键帧动画 (@keyframes):CSS3动画通过@keyframes规则定义动画过程,并利用animation属性实现动态效果。
  3. 动画属性:包括动画名称、持续时间、时间函数、延迟时间、播放次数等,这些属性定义了动画的起始状态、结束状态和动画执行过程。

性能分析

  1. 性能瓶颈:布局(Reflow)和重绘(Repaint)是动画性能的关键瓶颈。CSS动画涉及元素位置或尺寸的变化时,浏览器需要重新计算页面布局,代价较高;涉及颜色、边框、阴影等视觉属性的变化时,会导致重新绘制,影响性能。
  2. GPU加速:通过使用transformopacity属性,可以触发硬件加速,提高动画性能,因为它们不会触发重排或重绘,只会触发合成(Composite)。
  3. 减少重绘和重排:优化目标是减少重绘和重排,尽量让浏览器将动画交由GPU加速处理。

性能优化技巧

  1. 使用transformopacity:动画中应优先使用transformopacity,因为它们不会触发重排或重绘。
  2. 避免触发重排的属性:避免使用topleftmarginwidthheight等会触发布局(Reflow)的属性。
  3. 利用硬件加速:通过设置相关属性,让浏览器利用图形处理器来加速动画渲染。
  4. 减少DOM操作和样式变化:避免重绘和回流,提高动画性能。
  5. 使用CSS预处理器:使用Sass、Less等CSS预处理器,可以更好地组织和管理动画样式,提高开发效率。
  6. 内容优化和分层优化:使用contain属性限制元素的布局、绘制和大小影响,以及使用isolation属性创建新的层叠上下文,减少动画对主线程的影响。
  7. 条件性能优化:基于设备性能和视口大小进行优化,例如使用媒体查询减少动画持续时间或简化动画效果。

重绘重排

在Web开发中,特别是在浏览器渲染页面的过程中,”重绘”(Repaint)和”重排”(Reflow)是两个重要的性能优化概念。它们描述了浏览器在处理DOM变化时的不同阶段:

重排(Reflow)

重排是指浏览器重新计算元素的几何属性(位置、大小等),并重新排列页面元素的过程。当DOM结构发生变化时,如元素的增加、删除、移动或改变尺寸(例如,文本改变导致宽度变化),浏览器需要重新计算这些元素的布局。
触发重排的操作

  • 元素的尺寸或位置改变(如通过JavaScript修改元素的widthheighttopleft等属性)。
  • 页面加载(初始渲染)。
  • 浏览器窗口尺寸改变(如用户调整浏览器窗口大小)。
  • 内容变化(如文本输入、图片加载完成)。
    重排的影响
  • 重排是性能开销较大的操作,因为它可能需要遍历整个DOM树,计算所有元素的位置和尺寸。
  • 频繁的重排会导致页面渲染性能问题,尤其是在复杂的页面或动画效果中。

重绘(Repaint)

重绘是指在元素的几何属性没有变化,但是元素的外观(如颜色、阴影、边框颜色等)发生变化时,浏览器需要重新绘制元素的过程。
触发重绘的操作

  • 改变元素的颜色、背景色、边框颜色等不影响元素尺寸的属性。
  • 改变元素的透明度。
  • 应用CSS滤镜效果。
    重绘的影响
  • 重绘通常比重排的开销小,因为它不需要重新计算元素的位置和尺寸。
  • 但是,如果页面中有大量的元素需要重绘,或者重绘操作非常频繁,也可能导致性能问题。

性能优化

为了提高页面性能,减少重排和重绘的开销,可以采取以下措施:

  • 减少不必要的DOM操作,特别是在动画和高频更新的场景中。
  • 使用CSS的transformopacity属性进行动画,因为它们通常只触发重绘,而不触发重排。
  • 使用文档片段(DocumentFragment)或display: none元素进行批量DOM操作,以减少重排和重绘的次数。
  • 使用CSS的will-change属性来提示浏览器某个元素即将发生变化,从而优化性能。
  • 避免在页面的高频更新区域使用复杂的CSS选择器,以减少计算成本。
    理解重排和重绘的概念对于优化Web页面的性能至关重要,尤其是在处理复杂的页面布局和动画效果时。

animation和transition

animationtransition 都是 CSS 中用于创建动画效果的属性,但它们在用途和行为上有所不同。

CSS Transitions(过渡)

transition 用于在 CSS 属性值之间创建平滑的过渡效果。它主要用于实现元素状态之间的动态变化,比如当用户悬停在按钮上时改变背景颜色,或者在页面加载时淡入淡出元素。
主要特点:

  • 用于创建状态之间的过渡效果。
  • 通常用于响应用户交互,如 hover、focus 等。
  • 可以指定开始和结束状态,浏览器自动处理中间的过渡效果。
  • 可以控制过渡的持续时间、延迟和缓动函数。
    基本语法:
1
2
3
4
5
6
/* 选择器 {
transition-property: 属性名;
transition-duration: 持续时间;
transition-timing-function: 缓动函数;
transition-delay: 延迟时间;
} */

示例:

1
2
3
4
5
6
7
button {
background-color: blue;
transition: background-color 0.5s ease-in-out;
}
button:hover {
background-color: red;
}

CSS Animations(动画)

animation 用于创建更复杂的动画序列,可以包含多个关键帧,实现更复杂的动画效果,比如循环的旋转动画或者序列动画。
主要特点:

  • 用于创建更复杂的动画序列。
  • 可以包含多个关键帧(@keyframes)。
  • 可以控制动画的播放次数、方向、延迟等。
  • 可以创建无限循环的动画。
    基本语法:
1
2
3
4
5
6
7
8
9
10
11
12
/* @keyframes 名称 {
关键帧代码块
} */
/* 选择器 {
animation-name: 名称;
animation-duration: 持续时间;
animation-timing-function: 缓动函数;
animation-delay: 延迟时间;
animation-iteration-count: 播放次数;
animation-direction: 播放方向;
animation-fill-mode: 填充模式;
} */

示例:

1
2
3
4
5
6
7
8
9
10
11
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
div {
animation: spin 2s linear infinite;
}

区别总结:

  • transition 适用于简单的状态变化,通常与用户交互相关。
  • animation 适用于复杂的动画序列,可以包含多个状态和关键帧。
  • transition 通常用于实现平滑的视觉效果,而 animation 可以创建更丰富的动画效果。
    两者可以根据需要结合使用,以实现更丰富的用户界面动画效果。

css动画 GPU加速

在 CSS 动画中实现 GPU 加速可以显著提升动画性能,减少卡顿,提高用户体验。以下是实现 CSS 动画 GPU 加速的关键技术和优化建议:

1. 使用 GPU 友好的 CSS 属性

某些 CSS 属性会触发 GPU 加速,而另一些则会引发重排(Reflow)或重绘(Repaint),导致性能下降。推荐使用以下属性:

  • transform:用于平移、旋转、缩放等变换。
    1
    2
    3
    4
    5
    6
    7
    .box {
    transition: transform 0.5s ease;
    transform: translate3d(0, 0, 0); /* 触发 GPU 加速 */
    }
    .box:hover {
    transform: scale(1.2);
    }
  • opacity:用于透明度变化。
    1
    2
    3
    4
    5
    6
    7
    .box {
    transition: opacity 0.5s ease;
    opacity: 1;
    }
    .box:hover {
    opacity: 0.5;
    }

2. 强制开启 GPU 加速

对于不需要上述属性但需要 GPU 加速的元素,可以使用以下技巧强制触发硬件加速:

  • 使用 translateZ(0)translate3d(0, 0, 0)
    1
    2
    3
    .box {
    transform: translateZ(0);
    }
  • 使用 will-change 属性提前告知浏览器将要发生变化。
    1
    2
    3
    .box {
    will-change: transform, opacity;
    }

3. 优化动画实现

  • 避免使用 topleftmargin 等属性:这些属性会触发布局重排,性能较差。
  • 减少重绘和重排:批量修改样式,避免频繁读写 DOM。
  • 降低动画复杂度:减少同时动画的元素数量,避免长时间占用资源。

4. 使用 requestAnimationFrame

在 JavaScript 中实现动画时,推荐使用 requestAnimationFrame 替代 setIntervalsetTimeout,以确保动画在浏览器的刷新频率下运行,避免丢帧。

1
2
3
4
5
6
7
8
9
10
11
12
13
function animate() {
const box = document.querySelector('.box');
let position = 0;
function move() {
position += 2;
box.style.transform = `translateX(${position}px)`;
if (position < 500) {
requestAnimationFrame(move);
}
}
requestAnimationFrame(move);
}
animate();

5. 分离图层

通过将动画元素分离到独立图层,浏览器可以利用 GPU 渲染该元素,避免与其他元素争抢资源。

1
2
3
.box {
transform: translateZ(0); /* 创建独立图层 */
}

6. 注意事项

  • 避免滥用 GPU 加速:过度使用 GPU 加速可能导致内存消耗过大,尤其是在移动端。
  • 注意字体抗锯齿问题:GPU 渲染可能影响字体的抗锯齿效果。

7. 实战示例

以下是一个高性能动画的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>高性能 CSS 动画</title>
<style>
.box {
width: 100px;
height: 100px;
background-color: #4caf50;
transition: transform 0.3s ease, opacity 0.3s ease;
will-change: transform, opacity;
transform: translateZ(0); /* 触发 GPU 加速 */
}
.box:hover {
transform: scale(1.2);
opacity: 0.8;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>

通过以上方法,可以显著提升 CSS 动画的性能,让网页动效更加流畅。

requestAnimationFrame

当然!requestAnimationFrame 是一个用于高效实现动画的浏览器 API,它会在浏览器重绘之间更新动画,确保动画的平滑性并减少性能问题。以下是一个简单的 requestAnimationFrame 示例,展示如何使用它来实现一个平滑的动画效果。

示例:使用 requestAnimationFrame 实现一个元素的水平移动动画

HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>requestAnimationFrame 示例</title>
<style>
.box {
width: 50px;
height: 50px;
background-color: red;
position: absolute;
top: 50px;
left: 0;
}
</style>
</head>
<body>
<div class="box"></div>
<script src="script.js"></script>
</body>
</html>

JavaScript (script.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 获取元素
const box = document.querySelector('.box');
// 初始化位置
let position = 0;
// 动画函数
function animate() {
// 更新位置
position += 1; // 每次动画移动 1px
// 更新元素的样式
box.style.left = `${position}px`;
// 判断是否到达右边界(例如屏幕宽度)
if (position < window.innerWidth - 50) {
// 使用 requestAnimationFrame 调用自身,形成循环动画
requestAnimationFrame(animate);
} else {
console.log("动画结束");
}
}
// 开始动画
animate();

示例说明

  1. HTML 部分
    • 定义了一个 div 元素,类名为 box,并设置了初始位置(left: 0)。
    • 使用 position: absolute 使其可以脱离文档流并动态调整位置。
  2. CSS 部分
    • 设置了 box 的宽高和背景颜色。
    • 使用 position: absolute 以便通过 left 属性动态调整位置。
  3. JavaScript 部分
    • 获取 box 元素。
    • 定义一个变量 position,用于记录当前水平位置。
    • 定义 animate 函数:
      • 每次调用时,将 position 增加 1px。
      • 更新 boxleft 属性,使其水平移动。
      • 使用 requestAnimationFrame 在浏览器重绘之前调用自身,形成循环动画。
    • position 达到屏幕宽度时,动画停止。

关键点

  • requestAnimationFrame
    • 它会在浏览器的下一次重绘之前调用指定的回调函数,确保动画与屏幕刷新率同步。
    • setIntervalsetTimeout 更高效,因为它会自动暂停动画(例如在标签页隐藏时),避免浪费资源。
  • 性能优势
    • 动画与屏幕刷新率同步,避免丢帧。
    • 适合复杂的动画场景,尤其是涉及大量 DOM 操作时。

运行效果

  • 页面加载后,box 元素会从左侧开始水平移动,直到到达屏幕右侧边缘。
  • 动画非常平滑,因为它是通过 requestAnimationFrame 实现的,与浏览器的渲染机制紧密配合。
    这个示例展示了 requestAnimationFrame 的基本用法,你可以根据需要扩展它,比如添加动画的暂停/恢复功能,或者实现更复杂的动画路径。