【JS】实现平滑过渡的轮播图

3,627 阅读3分钟

上次实现了一个简单的轮播图 demo,不足之处在于从最后一张图片切换到第一张图片是通过直接切换到第一张来实现的,这次对其进行了优化,实现了从最后一张切换到第一张和从第一张切换到最后一张的平滑切换!

基本功能

  • 自动切换
  • 底部圆点切换
  • 左右按钮切换,节流处理
  • 鼠标放在按钮或圆点上,停止轮播,移开鼠标接着播放

思路

与普通轮播图思路相同,不过这个头和末尾分别加了两张图片用来过渡,也是图片移动到临界值时,取消动画效果,图片列表的 left 值改变,即使其展示的是同一张图片,但是图片列表的位置发生改变

未命名文件.png

  • 在实现轮播图时,在第一张图片前添加最后一张图片,在最后一张图片后添加第一张图片。a、b、c、d表示不同的图片
  • 在状态A中,为轮播图启动时的效果,展示第一张图片
  • 在状态B中,展示第二张图片
  • 在状态C中,这时已经是当前图片列表的最后一张了,此时需要将状态C变为状态D,使它展示的还是第一张图片,相当于只是悄悄把图片列表的left值变化,不开启动画,展示的图片并没有变还是第一张图片。
  • 上一张也是相同的思路,假如当前图片是图片列表中的第一张,此时需要切换到最后一张图片,即不开启动画,把图片列表的left值进行变化,展示的图片其实还是最后一张图片

HTML

<div id="container">
    <!-- 轮播图 -->
    <ul id="img_ul">
        <!-- 与之前相比,前后多了两张图片 -->
        <li><img src="./耿鬼.jpg"></li>
        <li><img src="./nxt.jpeg"></li>
        <li><img src="./raw.jpeg"></li>
        <li><img src="./smackdown.jpeg"></li>
        <li><img src="./耿鬼.jpg"></li>
        <li><img src="./nxt.jpeg"></li>
    </ul>
    <!-- 底部按钮 -->
    <ul id="litCir_ul"></ul>
    <!-- 左右切换按钮 -->
    <div id="buttons">
        <span id="left">&lt;</span>
        <span id="right">&gt;</span>
    </div>
</div>

CSS

需要注意的一点是,需要给包裹轮播图的 container 设置 overflow: hidden,来隐藏超出的图片。 并且图片列表的 left 值不能为0,为0展示的是最后一张图片,应该为 -width px。在该demo中即为 -500px

<style>
    /* 包裹轮播图的容器需要使用 overflow: hidden 来隐藏超出的图片 */
    #container {
        position: relative;
        width: 500px;
        height: 400px;
        margin: 0 auto;
        overflow: hidden;
        margin: 0;
        padding: 0;
    }
    
    ul {
        list-style: none;
        margin: 0;
        padding: 0;
    }
    
    #img_ul {
        width: 3000px; /* 轮播图宽度为 图片数 * 图片宽 */
        height: 400px;
        position: absolute;
        top: 0;
        left: -500px; /* 图片列表的默认 left 值 */
        transition: all .5s;
    }
    
    #img_ul li {
        float: left;
        margin: 0;
        padding: 0;
        width: 500px;
        height: 400px;
    }
    
    #img_ul li img {
        width: 500px;
        height: 400px;
    }
    
    #litCir_ul {
        position: absolute;
        margin: 0;
        padding: 0;
        right: 10px;
        bottom: 10px;
    }
    
    #litCir_ul li {
        margin: 0;
        padding: 0;
        float: left;
        width: 20px;
        height: 20px;
        text-align: center;
        line-height: 20px;
        border-radius: 50%;
        margin-left: 10px;
        cursor: pointer;
    }
    
    /* 圆点按钮生效样式 */
    li.active {
        background-color: white;
    }
    
    li.quiet {
        background-color: #1e90ff;
    }
    
    #buttons {
        margin: 0;
        padding: 0;
        display: none;
    }
    
    /* hover 轮播图则显示左右切换按按钮 */
    #container:hover #buttons {
        display: block;
    }
    
    #buttons span {
        position: absolute;
        width: 40px;
        height: 40px;
        top: 50%;
        margin-top: -20px;
        line-height: 40px;
        text-align: center;
        font-weight: bold;
        font-family: Simsun;
        font-size: 30px;
        border: 1px solid #fff;
        opacity: 0.3;
        cursor: pointer;
        color: #fff;
        background: black;
    }

    #left {
        left: 5px;
    }

    #right {
        left: 100%;
        margin-left: -45px;
    }
</style>

JS 实现图片轮播

获取 HTML 中的对象和声明所需变量

var img_ul = document.getElementById("img_ul"); // 轮播图
var litCir_ul = document.getElementById("litCir_ul"); // 圆点按钮 ul
var buttons = document.getElementById("buttons"); // 左右切换按钮

var cLis = litCir_ul.children; // 圆点按钮
var len = img_ul.children.length - 2; // 真实图片数,图片列表有6张,真实图片数只有4张
var width = 500; //每张图片的宽度
var picN = 1; //当前显示的图片下标为1,展示第一张图片

根据图片数添加圆点按钮

// 根据图片数添加小圆点
for (let i = 0; i < len; ++i) 
    var a_li = document.createElement("li");
    a_li.className = 'quiet';
    litCir_ul.appendChild(a_li);
}
// 默认把第一个圆点按钮设置 active
litCir_ul.children[0].className = 'active';

触及小圆点切换图片

给每个小圆点绑定 mouseover 事件,通过小圆点的 index 来确定该切换到哪张图片。

// 触碰小圆点切换图片
for (var i = 0; i < len; i++) {
    // 给每个小圆点绑定 mouseover 事件
    cLis[i].onmouseover = function () {
        // 停止轮播
        clearInterval(img_ul.timer);
        for (var j = 0; j < len; j++) {
            cLis[j].className = "quiet";
        }
        this.className = "active";
        picN = i + 1; // 切换的图片下标
        img_ul.style.left = (picN * width * -1) + 'px'; // 切换图片
    }
    
    // 开启轮播
    cLis[i].onmouseout = function () {
        Roll();
    }
}

切换圆点函数

function setActiveSpot() {
    for (var j = 0; j < len; j++) {
        cLis[j].className = "quiet";
    }
    
    // 这里是 picN - 1 是因为下标picN的图片对应着下标为picN - 1的圆点
    cLis[picN - 1].classList = "active";
}

使用定时器实现自动轮播

定义 Roll 函数,并执行。 我这里切换图片是通过设置 img_ulleft 属性来控制的。

记录当前显示的图片下标,定时器执行一次就把图片下标 picN +1,然后设置 img_ul.style.transition = "all .5s"; 开启动画,设置 img_ul.style.left = (-1 * picN * width) + 'px' 对图片进行切换。

picN === len + 1 时,表明当前显示的是最后一张图片(即原始图片的第一张),此时需要从最后一张图片切换为第一张图片,把 pinN 重置为1,并开启一个定时器,定时器的时间需与 transition 动画的时间一致,当切换动画结束时,定时器执行,取消 transition 动画,并悄悄改变图片列表的 left 值,展示原始图片的第一张。

function Roll() {
    clearInterval(img_ul.timer);
    img_ul.timer = setInterval(() => {
        ++picN;
        img_ul.style.transition = "all .5s";
        img_ul.style.left = (picN * width * -1) + 'px';
        if (picN === len + 1) {
            picN = 1;
            setActiveSpot();
            setTimeout(() => {
                img_ul.style.transitionProperty = "none"; // 取消动画
                img_ul.style.left = -width + 'px'; // 改变 left 值
            }, 500)
        } else {
            setActiveSpot();
        }
    }, 2000)
}

Roll();

点击左右切换按钮切换图片

// 停止轮播
buttons.children[0].onmouseover = function () {
    clearInterval(img_ul.timer);
}

buttons.children[1].onmouseover = function () {
    clearInterval(img_ul.timer);
}

// 开启轮播
buttons.children[0].onmouseout = function () {
    Roll();
}

buttons.children[1].onmouseout = function () {
    Roll();
}

实现思路与前面 Roll 函数类似

// 节流函数
function throttle(fn, time) {
    let canRun = true;
    // 闭包
    return function () {
        if (!canRun) return;
        canRun = false;
        setTimeout(() => {
            fn.apply(this, arguments);
            canRun = true;
        }, time);
    }
}

// 上一张按钮
buttons.children[0].onclick = throttle(function () {
    --picN;
    img_ul.style.transition = "all .5s";
    img_ul.style.left = (picN * width * -1) + 'px';
    if (picN === 0) {
        picN = len;
        setActiveSpot();
        setTimeout(() => {
            img_ul.style.transitionProperty = "none";
            img_ul.style.left = (picN * width * -1) + 'px';
        }, 500);
    } else {
        setActiveSpot();
    }
}, 500)

// 下一站按钮
buttons.children[1].onclick = throttle(function () {
    ++picN;
    img_ul.style.transition = "all .5s";
    img_ul.style.left = (picN * width * -1) + 'px';
    if (picN === len + 1) {
        picN = 1;
        setActiveSpot();
        setTimeout(() => {
            img_ul.style.transitionProperty = "none";
            img_ul.style.left = 0;
        }, 500);
    } else {
        setActiveSpot();
    }
}, 500)

最后

欢迎大家在评论区一起交流,一起进步!