如何实现平滑的box-shadow动画效果

你如何在 CSS 中设置box-shadow属性实现动画效果,既不会导致重新绘制每一帧,又不会严重影响你页面的性能?回答是:你并不能实现。因为box-shadow的动画变化会损害性能。

这里有一个简单的办法可以实现上述问题效果。如果要实现最小的重新绘制,应该创建一个伪元素并对其opacity元素进行动画处理,使其以每秒60帧的动画模仿运动物体相同的效果。

实例

认真观察这个实例,比较我们在其中使用的不同技巧。你是不是会说两者效果看起来一样。唯一不同的是我们如何应用阴影并对其进行动画处理。在左边实例中,我们鼠标hover(悬浮)时,对box-shadow应用了动画效果。而在右边的实例中,我们用::after添加了一个伪元素并对其设置了阴影,并对该元素的opacity元素进行了动画处理。

如果你使用开发工具尝试了其中之一,您应该会看到类似这样的东西 (绿色条表示已经绘制,其越少越好):

当你悬停在左边的卡片(在box-shadow上应用动画)与悬浮在右边的卡片(对其伪元素的opacity应用动画)进行相比时,你会很明显的发现有更多的重新绘制。

为什么我们会看到这种效果?有很少的 CSS 属性,即opacitytransform,进行动画处理时,不会不断触发重绘每一帧。我们最好坚持只在动画中更改这两个属性实现最小化重绘 (您的浏览器不得不做的工作)。

抛却其它的布局样式,这就是这两种技术之间的关键区别

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
26
27
/* The slow way */
.make-it-slow {
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
transition: box-shadow 0.3s ease-in-out;
}

/* 鼠标悬停实现更大阴影的过渡 */
.make-it-slow:hover {
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}

/* The fast way */
.make-it-fast {
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}

/* 设置更大的阴影并将之隐藏 */
.make-it-fast::after {
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
opacity: 0;
transition: opacity 0.3s ease-in-out;
}

/* 鼠标悬停时实现更大阴影的过渡显示 */
.make-it-fast:hover::after {
opacity: 1;
}

详细分解

基于上面所述,让我们看看如何创建上面示例中演示的3D卡片效果。第一步是将阴影转移到到一个伪元素,就像我们上面所做的一样。让我们也给所有创建的卡片添加布局代码:

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
/* 创建一个简单白色盒子,并添加阴影 */
.box {
position: relative;
display: inline-block;
width: 100px;
height: 100px;
border-radius: 5px;
background-color: #fff;
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
transition: all 0.3s ease-in-out;
}

/* 创建隐藏的伪元素 */
/* 最终样式包含阴影 */
.box::after {
content: '';
position: absolute;
z-index: -1;
width: 100%;
height: 100%;
opacity: 0;
border-radius: 5px;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
transition: opacity 0.3s ease-in-out;
}

注意,我们要.box.box::after添加了transition元素,因为我们要对这两个元素进行动画处理:.box上应用transform元素,.box::after上应用opacity元素.

这些样式给我们展示了一个具有微妙box-shadow样式的白色盒子。这里.box::after上的阴影已经完全被隐藏,不能与盒子效果相互影响。

若要创建演示中相同的效果,现在我们需要做的是:当鼠标悬停在.box时,产生放大效果,并且在伪元素和阴影上实现淡化:

1
2
3
4
5
6
7
8
9
/* 放大盒子 */
.box:hover {
transform: scale(1.2, 1.2);
}

/* 伪元素更大阴影的淡化 */
.box:hover::after {
opacity: 1;
}

总之,这就是所有的CSS样式,包括不同浏览器的兼容样式和一些额外的自定义缓动效果:

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
26
27
28
29
30
31
32
33
34
35
36
.box {
position: relative;
display: inline-block;
width: 100px;
height: 100px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
border-radius: 5px;
-webkit-transition: all 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
transition: all 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
}

.box::after {
content: "";
border-radius: 5px;
position: absolute;
z-index: -1;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
opacity: 0;
-webkit-transition: all 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
transition: all 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
}

.box:hover {
-webkit-transform: scale(1.25, 1.25);
transform: scale(1.25, 1.25);
}

.box:hover::after {
opacity: 1;
}

显然这里与只使用box-shadow相比应用了大量的 CSS 来实现相同的动画效果,只是改进性能。这又何必呢?

即使你的桌面处理box-shadow动画时没有任何问题,你的手机可能也不会出现问题。但是当你处理更为复杂的布局时,你就会发现你的桌面可能开始表现出症状。

保持只在过渡(transitions)和动画(animations)上应用transformopacity元素,你就会达到最佳的性能,那才是最佳的用户体验。

本文根据@Tobias的《How to animate “box-shadow” with silky smooth performance》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。

如需转载此译文,需注明英文出处:http://tobiasahlin.com/blog/how-to-animate-box-shadow/。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
原文: https://www.w3cplus.com/css3/how-to-animate-box-shadow.html © w3cplus.com


-------------本文结束感谢您的阅读-------------
感觉文章不错,就赏个吧!
0%