上周,我们发布了一个挑战性题目------ 纯 CSS 实现无限轮播 banner 效果,天猫首页官方效果是这样的:
今天我给你分享一下我的解题思路。
首先,我们要先把这个 banner 的基础样式先实现了。先来看下我们 banner 的结构:
这个 banner 主要 3 个部分:
- 【红色部分】banner 列表,使用一个
ul
无序列表结构 - 【绿色部分】左右切换按钮
- 【黄色部分】banner 序号显示器,跟 banner 列表一一对应,因此也是一个
ul
无序列表结构
结构确定下来了,基础样式就不详细说了,你可以自己去练习,或者直接参考我们官方参考答案的体验页面(点击这里查看)。
我们先来看最核心的 banner 切换效果实现。
通常,我们实现 banner 列表切换效果,有两种方案。
第一种方案是使用 float
或者 inline-block
让元素排成一行,然后给 ul
元素添加动画效果,让整个列表进行移动,实现 banner 切换效果,如下图所示:
第二种方案是绝对定位方案,它的原理是让所有 banner 元素都变成绝对定位,每个 banner 都可以自由控制定位,除了正在展示的 banner 之外,其他 banner 元素都在初始位置等待,当 banner 需要切换时,就同时给 正在展示的 banner 元素 和 即将展示的 banner 元素施加动画效果,完成切换效果,其他 banner 元素不动,还是在初始位置等待。
不管是哪种方案,都能实现无限滚动效果,我选择第 2 个方案,因为我嫌方案 1 要补元素,比较麻烦。
确定 banner 的样式实现和切换方案之后,我们看看切换动画方案。
根据我们在学习路线(戳此了解)上的学习,我们知道,CSS 动画效果主要是 transition
和 animation
,要使用哪个呢?
因为这里需要实现无限循环的动画效果,因此只能使用 animation
。
确定这个之后,我们首先来实现单个 banner 的切换效果实现:
css
@keyframes carousel1 {
0% {
left: 0;
}
100% {
left: -520px;
}
}
@keyframes carousel2 {
0% {
left: 520px;
}
100% {
left: 0;
}
}
.mbanner-list-item:nth-child(1) {
animation: carousel1 1s 1s forwards;
}
.mbanner-list-item:nth-child(2) {
animation: carousel2 1s 1s forwards;
}
这样,单个 banner 切换的效果就完成了:
好了,实现了单个 banner 的切换效果,下一步就是要串起来,怎样实现完整的,可以连贯串起来的,无限循环的切换效果呢?
有练习这个题目,尝试解题的同学应该知道,最大的难点就是这里。
基础的 animation
语法不能实现这道题目的效果。主要因为它的 delay
参数只是控制最开始动画之前的停顿,它没办法在动画过程中设置一个停顿。而这道题最大的卡点就是 banner 完成切换之后,需要停顿一段时间。如果不需要停顿的话,就非常简单了。
那么,我们可以在 keyframes
中设置停顿吗?答案是可以的。
keyframes
就是让我们定义每个动画帧的样式的,然后 animation
帮我们把这些帧的样式实现一个平滑过渡,最终形成动画。
这就是 animation
的原理,因此,我们在设置动画帧样式的时候,定义某段时间的开始和结束两个动画帧的样式一样,那就等于是静止效果了,这就是本道题的关键解题思路,知道这个之后,我们就需要做一些计算了。
先来计算:
一次完整动画的时间 = (1s + 1s) * 3 = 6s
其中,每个 banner 切换动画要 1s,停顿展示要 1s,所以 3 个 banner 的一次完整动画的时间是 6s。
为了方便后面的讲解,就假定这些时间值,实际上,这两个时间值都是可以任意设定的
因为我们选择 banner 切换的方案是绝对定位方案,因此,我们动画样式的关键是每个 banner 的 left
属性值,也就是位置信息。
那么我们再来看每 1s 的开始,各个 banner 的位置:
上图详细梳理了每个 banner 在每个关键时刻的位置。动画总时长是 6s,所以每秒的占比是 1/6 约等于 16.66%。
另外需要注意的是,当需要切换 banner 的时候,新 banner 要立刻改变自己的位置到切换前的位置。
因此这里每一秒都要区分每秒开始和每秒结束两个动画帧。
比如,当需要执行 banner2 切换 banner3 的时候,我们注意 banner3 的位置变化(图中绿色方块)。
在第 2s 结束时刻,banner3 的位置是 -520px
,也就是在视窗左边。
在第 3s 开始时刻,banner3 的位置需要突变到 520px
,也就是跳到视窗右边,然后准备开始执行左滑动画了。
这就是所有的关键点了,在上图中,我把每一秒的开始和结束时刻的动画帧快照都画出来了。
根据这个,我们就可以确定每一个 banner 在一次完整的动画过程中,各个关键帧的位置了,我们把这个用代码实现出来,就是:
css
@keyframes carousel1 {
0% {
left: 0;
}
16.67%, 66.66% {
left: -520px;
}
66.67%{
left: 520px;
}
83.33%, 100% {
left: 0;
}
}
@keyframes carousel2 {
0% {
left: 520px;
}
16.67%, 33.33% {
left: 0;
}
50%, 100% {
left: -520px;
}
}
@keyframes carousel3 {
0%, 33.33% {
left: 520px;
}
50%, 66.67% {
left: 0;
}
83.33%, 100% {
left: -520px;
}
}
.mbanner-list-item:nth-child(1) {
animation: carousel1 6s 1s infinite forwards;
}
.mbanner-list-item:nth-child(2) {
animation: carousel2 6s 1s infinite forwards;
}
.mbanner-list-item:nth-child(3) {
animation: carousel3 6s 1s infinite forwards;
}
效果如下:
由于要压缩 gif 图片的大小,所以上面是加速之后的,你可以自己编写代码来查看效果。
最后,我们还剩下底部的 banner 序号显示器了,利用同样的原理就可以实现了,具体的我就不画图了,直接给代码:
css
@keyframes pagination1 {
0%, 16.66% {
background-color: rgba(255, 80, 0);
}
16.67%, 83.33% {
background-color: rgba(255, 255, 255, 0.5);
}
83.34%, 100% {
background-color: rgba(255, 80, 0);
}
}
@keyframes pagination2 {
0%, 16.66% {
background-color: rgba(255, 255, 255, 0.5);
}
16.67%, 50% {
background-color: rgba(255, 80, 0);
}
50.01%, 100% {
background-color: rgba(255, 255, 255, 0.5);
}
}
@keyframes pagination3 {
0%, 50% {
background-color: rgba(255, 255, 255, 0.5);
}
50.01%, 83.33% {
background-color: rgba(255, 80, 0);
}
83.34%, 100% {
background-color: rgba(255, 255, 255, 0.5);
}
}
.mbanner-pagination-item:nth-child(1) {
animation: pagination1 6s 1s infinite forwards;
}
.mbanner-pagination-item:nth-child(2) {
animation: pagination2 6s 1s infinite forwards;
}
.mbanner-pagination-item:nth-child(3) {
animation: pagination3 6s 1s infinite forwards;
}
最终效果如下:
好了,这道题的详细解答思路就到这里,最终的效果你也可以到之道前端原创项目的官方参考答案体验网页(点击这里)查看。
如果你没有解出来这道题,可以根据我的解题思路去实现一遍,把代码写出来,看到最终效果,你肯定会有收获的。
另外,最前面我提到,banner 列表有 2 种实现方案,我这里使用的是方案 2,你可以尝试用同样的动画实现思路,去实现方案 1 的列表结构的无限循环动画效果,如果你能够实现出来,你会获得更多的成长。
最后,希望你可以通过这个项目,对 CSS animation
、 keyframes
和 动画帧 有更加深入的理解。
利用同样的原理,只要我们的动画是可以拆分成一个一个动画帧,而且每个动画帧的样式效果是可以用 CSS 来实现过渡的,基本上就可以使用纯 CSS 的方式来实现这个动画。
----------------【END】----------------
如果你是真心喜欢前端,并相信成长,想要提升自己的话,欢迎加入之道前端学习圈子。
戳这里 免费获取 之道前端的学习资料和专属服务。