纯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】----------------

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

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

相关推荐
mldong6 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排6 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60617 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角8 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!8 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰8 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi10 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen11 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马11 小时前
AI源码分析:拆解模型底层实现逻辑
前端
BillKu11 小时前
全局样式变量:CSS 自定义属性(--border-color)和SCSS 变量($border-color)的说明
javascript·css·scss