前言

轮播图在前端中的应用场景非常多、应用频率非常高,大到网站商城,小到个人主页,都会有用到轮播图的时候。

现在网上的轮播图插件也非常多,花样花式也各异,有基于jq的、基于框架,所以一般是不用我们去手写轮播图的。

但在某些情况下,我们还是需要去手写轮播图。

手写原生js轮播图,有助于我们知道轮播图的实现原理。

知道了原理,有时候我们也能根据自己的需求去修改下载下来插件的源码。

 

效果

我们先来看下轮播图的最终效果图:

根据上面的效果图,我们来总结一下我们要实现的轮播图的功能:

  • 箭头点击
  • 按钮点击
  • 滑动过渡
  • 自动播放

 

原理

轮播图的原理要配合html结构和css样式来讲。

我们先来看一下html结构:

<div class="slide-wrapper" id="slide_wrapper">
    <div class="slide-imgs" id="slide_imgs" style="left: -400px;">
        <img class="slide-img" src="slide-img3.jpg"><!-- 辅助过渡图 -->
        <img class="slide-img" src="slide-img0.jpg">
        <img class="slide-img" src="slide-img1.jpg">
        <img class="slide-img" src="slide-img2.jpg">
        <img class="slide-img" src="slide-img3.jpg">
        <img class="slide-img" src="slide-img0.jpg"><!-- 辅助过渡图 -->
    </div>
    <div class="slide-btns" id="slide_btns">
        <span class="slide-btn active" data-index="0"></span>
        <span class="slide-btn" data-index="1"></span>
        <span class="slide-btn" data-index="2"></span>
        <span class="slide-btn" data-index="3"></span>
    </div>
    <a class="slide-arrow" id="slide_left" href="#">&lt;</a>
    <a class="slide-arrow" id="slide_right" href="#">&gt;</a>
</div>

上图是我们整个轮播图的html结构,可以看出代码是很少的。

可以看到,我实现的轮播图用了4张图片,但代码中有6张,我用代码注释标记的2张图片是辅助图,用来辅助动画过渡,下面会讲到。

请注意观察 slide-wrapper 和 slide-imgs 。

另外可以发现 class="slide-imgs" 元素上有样式属性 left ,在html里写样式是为了方便我们待会在js中取该元素的 left 的值。

 

接下来我们配合在浏览器里的效果(按F12打开控制台)来讲解轮播图的原理:

从图片中可以看出,我们轮播图每张图片的宽度是400px,同样的,整个轮播图容器 .slide-wrapper 的宽度也是 400px,当然高度也是一样。

 

 

上面的图片是 .slide-imgs 元素,它是包裹我们所有图片的容器,它的宽度是2400px,这是因为我们有6张图片,每张图片是400px(上图因为太长我没有截出)。

 

接着就是设置 .slide-wrapper 元素的样式,将轮播图最外层的包裹元素 overflow: hidden; :

.slide-wrapper {
    position: relative;
    width: 400px;
    height: 250px;
    overflow: hidden;
}

 

轮播图的原理就是让“包裹图片的容器元素”(.slide-imgs元素)做位置偏移

每次偏移的距离是每张图片的宽度。

每次切换的时候,我们就重新改变 .slide-imgs元素 的样式属性 left 的值(将.slide-wrapper设置为相对定位,将.slide-imgs设置为绝对定位)。

 

从上图就可以看到所有图片的包裹元素进行了位置偏移,这就是轮播图的核心原理了。

 

箭头点击

var slide_left = document.getElementById('slide_left'),
    slide_right = document.getElementById('slide_right'),
    slide_imgs = document.getElementById('slide_imgs'),
slide_left.onclick = function() {
    ...

    imgChange(400);

    ...
};

slide_right.onclick = function() {
    ...

    imgChange(-400);

    ...
};

首先获取我们两个箭头按钮的元素,编写点击后要执行的代码,

每一次点击,就切换一张图片,切换的大小是图片的宽度,这里是400px。

左点击就传入负值,右点击就传入正值,然后执行图片切换的函数 imgChange() :

function imgChange(offset) {
    var newLeft = parseInt(slide_imgs.style.left) + offset, // 新坐标值

    ...

    slide_imgs.style.left = newLeft + 'px';
    if (newLeft > -400) {
        slide_imgs.style.left = -1600 + 'px';
    }
    if (newLeft < -1600) {
        slide_imgs.style.left = -400 + 'px';
    }

    ...

}

 

imgChange()函数的作用是:

利用的传入的偏移值,计算出 .slide-imgs元素 该具有的新的位置值(left值),值计算出来后,再设置 . slide-imgs元素 的样式属性。

当向左切换,切换超出第一张图片的时候,将 .slide-imgs元素 的位置赋值为最后第四张图片的位置值;

当向右切换,切换超出第四张图片的时候,将 .slide-imgs元素 的位置赋值为最后第一张图片的位置值(如上面的if语句),这样就能实现循环切换。

 

按钮点击

当点击按钮的时候我们要做两件事情:

1.切换到对应的图片;

2.改变按钮的样式。

要做到这两件事我们就需要一个值 slide_index 来记住当前图片和圆点的位置索引:

<div class="slide-btns" id="slide_btns">
    <span class="slide-btn active" data-index="0"></span>
    <span class="slide-btn" data-index="1"></span>
    <span class="slide-btn" data-index="2"></span>
    <span class="slide-btn" data-index="3"></span>
</div>
slide_btns = document.getElementById('slide_btns').getElementsByTagName('span'),
slide_wrapper = document.getElementById('slide_wrapper'),
slide_index = 0,  // 当前图片或圆点的索引
for (var i=0; i<slide_btns.length; i++) {
    slide_btns[i].onclick = function() {
        
        ...

        var data_index = parseInt(this.getAttribute('data-index'));
        var offset = -400 * (data_index - slide_index);

        ...

        imgChange(offset);
        slide_index = data_index;
        btnChange();
    };
}

 

我们先完成第一件事:切换到对应的图片。

从html代码可以看出,我们给每一个按钮都赋予了相应的位置索引值 data-index。

接着,我们再给每一个按钮添加点击事件,当点击按钮的时候,我们再调用imgChange()函数,并传入正确的偏移量的值。

因为点击按钮的时候是可以跳跃性地点击,比如点第一个按钮后点第二个按钮,但也可以点第一个按钮后就点第四个按钮,

所以传入的偏移量通过这句代码来计算:

// data_index是点击的按钮的位置索引
// slide_index是当前图片的位置索引
var
offset = -400 * (data_index - slide_index);

 

所以当我们执行imgChange()函数后要记得更新当前图片的索引值:

slide_index = data_index;

 

最后是完成第二件事:改变按钮的样式:

function btnChange() {
    for (var i=0; i<slide_btns.length; i++) {
        if (slide_btns[i].className = 'slide-btn active') {
            slide_btns[i].className = 'slide-btn';
        }
    }
    slide_btns[slide_index].className = 'slide-btn active';
}

 

分析一下上面的代码,事先将选中按钮的样式 .active 写好:

.active {
    background-color: #20217e;
}

 

再改变选中按钮的类名(className),赋予它 active :

slide_btns[slide_index].className = 'slide-btn active';

 

前提,我们要先利用循环清除每一个按钮的 .active ,这样才不会出现多个选中按钮:

for (var i=0; i<slide_btns.length; i++) {
    if (slide_btns[i].className = 'slide-btn active') {
        slide_btns[i].className = 'slide-btn';
    }
}

 

滑动过渡

上面我们所实现都效果都是一瞬间的,就是一瞬间就切换到下一张图片,并没有一种过渡的视觉体验。

如何实现这种滑动过渡的效果?

我们知道,轮播图的核心原理是位置偏移,就是从一个位置到另一个位置的改变,

如果我们为这种改变加上一段时间,假设这个时间是2s,那么完成这个变化就得花2s时间,就不是一瞬间完成了,

这个完成过程就是过渡,这个过渡因为时间够长,所以能被我们所观察到。

 

好了,现在我们有一段时间 time 了,但还不够,

因为滑动的实质是慢慢地偏移,将一段完整的偏移量分为几次小的偏移量,

假设我们的 time = 500ms,我们每一次所花时间 perTime = 10ms,

所以在500ms内,我们要偏移多少次 per = time / perTime ,

每一次偏移的距离为 perOffset = offset / per 。

一直地偏移,如果还没到达偏移的目标量时,还是继续偏移,

这里我们用一个延时定时器和递归来达到这个效果,每 10ms 就进行一次偏移:

function imgChange(offset) {
    var newLeft = parseInt(slide_imgs.style.left) + offset, // 新坐标值
        time = 500,                                         // 总移动时间
        perTime = 10,                                       // 每次移动的时间
        per = time / perTime,                               // 移动多少次
        perOffset = offset / per;                           // 每次移动的距离
        animate = function() {
            animated = true;
            // 注意这里的parseInt(slide_imgs.style.left)是一个实时的值,不能赋给一个变量以节省代码
            if ((perOffset < 0 && parseInt(slide_imgs.style.left) > newLeft) || 
                (perOffset > 0 && parseInt(slide_imgs.style.left) < newLeft)) {

                slide_imgs.style.left = parseInt(slide_imgs.style.left) + perOffset + 'px';
                setTimeout(animate, perTime);

            } else {
                animated = false;
                slide_imgs.style.left = newLeft + 'px';
                if (newLeft > -400) {
                    slide_imgs.style.left = -1600 + 'px';
                }
                if (newLeft < -1600) {
                    slide_imgs.style.left = -400 + 'px';
                }
            }
        };
    animate();
}

 

两张辅助图

如果没有设置辅助图做衔接的话,假设从第四张图滑向第一张图,第四张图会先滑向一片空白区域:

而不是

 

自动播放

用一个定时器自动地去调用箭头点击函数:

function autoPlay() {
    timer = setInterval(function() {
        slide_right.onclick();
    }, 2000);
}

...

autoPlay(); // 在结尾的时候调用自动播放函数

 

源码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>原生js轮播图</title>
    <style>
        .slide-wrapper {
            position: relative;
            width: 400px;
            height: 250px;
            overflow: hidden;
        }
        .slide-imgs {
            position: absolute;
            z-index: 1;
            width: 2400px;
            height: 250px;
        }
        .slide-img {
            float: left;
            width: 400px;
            height: 250px;
        }
        .slide-btns {
            position: absolute;
            right: 0;
            bottom: 5px;
            left: 0;
            z-index: 2;
            width: 60px;
            margin: auto;
        }
        .slide-btn {
            display: inline-block;
            float: left;
            width: 10px;
            height: 10px;
            margin: 0 2px;
            background-color: #fff;
            border-radius: 50%;
            cursor: pointer;
        }
        .active {
            background-color: #20217e;
        }
        .slide-arrow {
            position: absolute;
            top: 0;
            bottom: 0;
            z-index: 2;
            width: 23px;
            height: 23px;
            margin: auto;
            color: #fff;
            font-size: 30px;
            font-weight: bold;
            line-height: 23px;
            text-decoration: none;
        }
        #slide_left {
            left: 5px;
        }
        #slide_right {
            right: 5px;
        }
    </style>
</head>
<body>
    <div class="slide-wrapper" id="slide_wrapper">
        <div class="slide-imgs" id="slide_imgs" style="left: -400px;">
            <img class="slide-img" src="slide-img3.jpg"><!-- 辅助过渡图 -->
            <img class="slide-img" src="slide-img0.jpg">
            <img class="slide-img" src="slide-img1.jpg">
            <img class="slide-img" src="slide-img2.jpg">
            <img class="slide-img" src="slide-img3.jpg">
            <img class="slide-img" src="slide-img0.jpg"><!-- 辅助过渡图 -->
        </div>
        <div class="slide-btns" id="slide_btns">
            <span class="slide-btn active" data-index="0"></span>
            <span class="slide-btn" data-index="1"></span>
            <span class="slide-btn" data-index="2"></span>
            <span class="slide-btn" data-index="3"></span>
        </div>
        <a class="slide-arrow" id="slide_left" href="#">&lt;</a>
        <a class="slide-arrow" id="slide_right" href="#">&gt;</a>
    </div>
    <script>

        var slide_left = document.getElementById('slide_left'),
            slide_right = document.getElementById('slide_right'),
            slide_imgs = document.getElementById('slide_imgs'),
            slide_btns = document.getElementById('slide_btns').getElementsByTagName('span'),
            slide_wrapper = document.getElementById('slide_wrapper'),
            slide_index = 0,  // 当前图片或圆点的索引
            animated = false, // 标记是否处于动画状态
            timer = null;
            

        function imgChange(offset) {
            var newLeft = parseInt(slide_imgs.style.left) + offset, // 新坐标值
                time = 500,                                         // 总移动时间
                perTime = 10,                                       // 每次移动的时间
                per = time / perTime,                               // 移动多少次
                perOffset = offset / per;                           // 每次移动的距离
                animate = function() {
                    animated = true;
                    // 注意这里的parseInt(slide_imgs.style.left)是一个实时的值,不能赋给一个变量以节省代码
                    if ((perOffset < 0 && parseInt(slide_imgs.style.left) > newLeft) || 
                        (perOffset > 0 && parseInt(slide_imgs.style.left) < newLeft)) {

                        slide_imgs.style.left = parseInt(slide_imgs.style.left) + perOffset + 'px';
                        setTimeout(animate, perTime);

                    } else {
                        animated = false;
                        slide_imgs.style.left = newLeft + 'px';
                        if (newLeft > -400) {
                            slide_imgs.style.left = -1600 + 'px';
                        }
                        if (newLeft < -1600) {
                            slide_imgs.style.left = -400 + 'px';
                        }
                    }
                };
            animate();
        }

        function btnChange() {
            for (var i=0; i<slide_btns.length; i++) {
                if (slide_btns[i].className = 'slide-btn active') {
                    slide_btns[i].className = 'slide-btn';
                }
            }
            slide_btns[slide_index].className = 'slide-btn active';
        }

        function autoPlay() {
            timer = setInterval(function() {
                slide_right.onclick();
            }, 2000);
        }

        function stopPlay() {
            clearInterval(timer);
        }

        slide_left.onclick = function() {
            if (!animated) { // 防止快速点击后出现BUG
                imgChange(400);
                if (slide_index == 0) {
                    slide_index = 3;
                } else {
                    slide_index -= 1;
                }
                btnChange();
            }
        };

        slide_right.onclick = function() {
            if (!animated) {
                imgChange(-400);
                if (slide_index == 3) {
                    slide_index = 0;
                } else {
                    slide_index += 1;
                }
                btnChange();
            }
        };

        for (var i=0; i<slide_btns.length; i++) {
            slide_btns[i].onclick = function() {
                if (this.className != 'slide-btn active') {
                    var data_index = parseInt(this.getAttribute('data-index'));
                    var offset = -400 * (data_index - slide_index);
                    if (!animated) {
                        imgChange(offset);
                        slide_index = data_index;
                        btnChange();
                    }
                }
            };
        }

        slide_wrapper.onmouseover = stopPlay;
        slide_wrapper.onmouseout = autoPlay;

        autoPlay();

    </script>
</body>
</html>
View Code

 

参考视频:https://www.imooc.com/learn/18

内容来源于网络如有侵权请私信删除
你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!

相关课程