说起前端页面轮播效果,想必大伙都不会陌生,图文并茂的说明再加上可以定时切换的特点, 让几乎每个展示自己产品或者设计的网站都有这一模块 。传统的Web开发教学中都会讲到利用JavaScript实现轮播效果,但是有没有想过用CSS去实现它呢?实际上这是可行的,一起来看看吧。

Beautiful Vegan Food Design
(图片来自 Pinterest

1. 思路分析

在开始写之前,首先要理清CSS实现轮播的思路:使用animation动画定时切换样式。

所以在做这个效果之前,需要有CSS动画相关的知识,具体内容可以参考菜鸟教程上的animation动画教学

2. 开始实现

现在,我们从最简单的效果开始 。

2.1 基础效果

最简单的实现就是定时切换区域的背景图,HTML结构写出来是这样子的:

<div class="carousel-img" style="background-image: url(test.jpg)"></div>

只要定时改变background-image属性值就OK了,CSS样式如下:

.carousel-img{
   width:130px;
   height:100px;
   background-repeat:no-repeat;
   background-position:center center;
   background-size:cover;
   animation:carousel 12s linear infinite normal
}
@keyframes carousel {
    0%,100%{
       background-image: url(test.jpg)
    }
    25%{
        background-image: url(test1.jpg)
    }
    50%{
        background-image: url(test2.jpg)
    }
    75%{
        background-image: url(test3.jpg)
    }
}

demo预览效果如下:

如果你足够细心,这时候就会发现一个问题:当这个效果第一次载入的时候,第一张跟第二张图片的间隔非常短,往后切换就没有这种问题。观察Firefox浏览器的CSS动画查看器,可以发现这个动画的进度图是这样子的:

Firefox浏览器开发者工具显示的CSS动画示意图
Firefox浏览器开发者工具显示的CSS动画示意图

其中紫色点表示CSS规则中指定的各个时段的状态,蓝绿色的阶梯状表示动画的过程,每个阶梯的拐角位置(红色箭头位置)表示图片切换的时间点,在这张图中,拐角位置位于每两个紫色点的正中间。可以看到第一次图片切换的时候,动画才运行了12.5%,第二次切换在37.5%,第三次在62.5%,第四次在87.5%,然后再下一个周期的12.5%处再次切换。

CSS动画与JavaScript动画的区别在于,使用@keyframes声明的animation动画,我们指定的是一个状态,即动画运行到50%的时候应该处于一个什么状态,而在50%之前,动画就开始执行状态切换。但是JavaScript的动画,我们指定的是切换的时间,代码运行到这一行时才会执行切换这个命令,新的状态会在执行命令之后才能生效。

为了尽可能避免这种问题,可以在第一次切换点25%之前再加一句状态声明:20%{background-image: url(test.jpg)},这样在动画20%的时候还是维持着第一张图片的状态,也就是人为地让状态切换时间延后。

修改刚才的@keyframes代码段:

@keyframes carousel{
    0%,20%,100%{
       background-image: url(test.jpg)
    }
    25%,45%{
        background-image: url(test1.jpg)
    }
    50%,70%{
        background-image: url(test2.jpg)
    }
    75%,95%{
        background-image: url(test3.jpg)
    }
}

新的动画进度图:

Firefox浏览器开发者工具显示的CSS动画示意图
Firefox浏览器开发者工具显示的CSS动画示意图

可以看出来经过修改后的动画,图片切换时刻基本跟25%,50%,75%,100%吻合。

2.2 进阶效果

先来看目标效果demo,从第一张滑动到最后一张再从最后一张往回滑动到第一张:

带滑入滑出特效的轮播
QQ影音工具箱视频转GIF图的时候只能截取20s,而我原先动画规则里写了30s,所以没有完整显示出来

这样子带有滑入滑出效果的轮播,因为有了画面过渡,所以比单纯的图片切换在视觉体验上更流畅。

首先写HTML结构,注意这里这四张图其实在同一排的,滑动的时候使用transform: translateX属性进行移动。

<div class="carousel-img-container">
    <div class="carousel-img-scroll">
        <div class="carousel-img" style="background-image: url(test.jpg)"></div>
        <div class="carousel-img" style="background-image: url(test1.jpg)"></div>
        <div class="carousel-img" style="background-image: url(test2.jpg)"></div>
        <div class="carousel-img" style="background-image: url(test3.jpg)"></div>
    </div>
</div>

再加上初始样式,让这四张图并排显示:

.carousel-img-container {
    width: 130px;
    height: 100px;
    overflow: hidden;
}
.carousel-img-scroll {
    width: 400%;
    height: 100%;
    display: flex;
    animation: carousel 30s linear infinite normal;
}
.carousel-img {
    width: 25%;
    height: 100%;
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
}

接下来是动画规则部分,仿照上面一个效果,这样写(如果只写0%,25%,50%,75%,100%时候的状态,那么就等效于使用from…to…语法,动画全程都在移动,不会有停留的时候):

@keyframes carousel{
    0%,20%,100%{
       transform: translateX(0) 
    }
    25%,45%{
       transform: translateX(-25%)
    }
    50%,70%{
       transform: translateX(-50%)
    }
    75%,95%{
       transform:translateX(-75%)
    }
}

然而问题来了,95%到100%的状态,也就是transform:translateX(-75%)transform:translateX(0)这一切换,明显不符合期望的效果,一瞬间就从最后一张到第一张。

接着修改,既然要往返效果,那么修改animation属性中的animation-direction子属性为alternate(动画规则也需要修改)是不是就可以了?

animation: carousel 30s linear infinite alternate;
@keyframes carousel{
    0%,20%{
       transform: translateX(0); 
    }
    25%,45%{
       transform: translateX(-25%)
    }
    50%,70%{
       transform: translateX(-50%)
    }
    75%,100%{
       transform:translateX(-75%)
    }
}

但是这样又有新的问题出现了:动画在最后一张图停留了非常长的时间,因为一来一回都要在第四张图上停留,所以就有了双倍的时间。

我解决这个问题的方法是:animation-direction属性使用normal值,不依靠CSS内置的往返运动,把整个往返一个来回当成一个运动周期,手动指定一个周期中每个时间点的状态。

从头再来分析一下这个动画过程,它一个周期的过程是这样子:

图1 → 图2 → 图3 → 图4 → 图3 → 图2 → 图1

划分阶段应该划分6个,每个就占总长度的16.6%,修改刚才的@keyframes代码段:

@keyframes carousel{
    0% {
        transform: translateX(0)
    }
    16.6% {
        transform: translateX(-25%)
    }
    33.2% {
        transform: translateX(-50%)
    }
    49.8% {
        transform:translateX(-75%)
    }
    66.4% {
        transform:translateX(-50%)
    }
    83.0% {
        transform:translateX(-25%)
    }
    100% {
        transform:translateX(0)
    }
}

然后为了让动画在每张图片上都有一个停留,继续修改,在每个移动时间点之前添加一段维持当前状态的规则:

@keyframes carousel{
    0% {
        transform: translateX(0)
    }
    13%{
        transform: translateX(0)
    }
    16.6% {
        transform: translateX(-25%)
    }
    29.6%{
        transform: translateX(-25%)
    }
    33.2% {
        transform: translateX(-50%)
    }
    46.2%{
        transform: translateX(-50%)
    }
    49.8% {
        transform:translateX(-75%)
    }
    62.8%{
        transform:translateX(-75%)
    }
    66.4% {
        transform:translateX(-50%)
    }
    79.4%{
        transform:translateX(-50%)
    }
    83.0% {
        transform:translateX(-25%)
    }
    96%{
        transform:translateX(-25%)
    }
    100% {
        transform:translateX(0)
    }
}

现在,可以看看效果,是不是就跟一开始的demo一样呢。在这个动画中,为了图片滑动的效果能更流畅些,可以增加动画总时长,或者适当减少动画维持当前图片状态的时长(调整后来插入的时间点百分比即可),以留出更多时长给滑动效果。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据