纯CSS实现无限轮播banner,这道题你解出来了吗?

上周,我们发布了一个挑战性题目------ 纯 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 动画效果主要是 transitionanimation,要使用哪个呢?

因为这里需要实现无限循环的动画效果,因此只能使用 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 animationkeyframes 和 动画帧 有更加深入的理解。

利用同样的原理,只要我们的动画是可以拆分成一个一个动画帧,而且每个动画帧的样式效果是可以用 CSS 来实现过渡的,基本上就可以使用纯 CSS 的方式来实现这个动画

----------------【END】----------------

如果你是真心喜欢前端,并相信成长,想要提升自己的话,欢迎加入之道前端学习圈子。

戳这里 免费获取 之道前端的学习资料和专属服务。

相关推荐
随·枫4 分钟前
html渲染优先级
前端·html
小扳40 分钟前
Web 毕设篇-适合小白、初级入门练手的 Spring Boot Web 毕业设计项目:电影院后台管理系统(前后端源码 + 数据库 sql 脚本)
java·前端·数据库·spring boot·mysql·spring·课程设计
田本初40 分钟前
从0-1逐步搭建一个前端脚手架工具并发布到npm
前端·npm·node.js
Marshall35721 小时前
Canvas 和 SVG 的高级使用与性能优化
前端·svg·canvas
Java学长-kirito2 小时前
springboot/ssm网购平台管理系统Java在线购物商城管理平台web电商源码
java·前端·spring boot
夫琅禾费米线2 小时前
JavaScript 中的 Generator 函数及其方法
开发语言·前端·javascript
Traced back2 小时前
pinia的使用
前端
世界和平�����2 小时前
vue3 命名式(函数式)弹窗
前端·javascript·vue.js
所遇所思3 小时前
vue项目中中怎么获取环境变量
前端·javascript·vue.js
ljklxlj3 小时前
webview4/edgewebbrower学习记录——执行js
前端·javascript·学习