欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

原生js轮播图

程序员文章站 2022-06-24 22:06:34
前言 轮播图在前端中的应用场景非常多、应用频率非常高,大到网站商城,小到个人主页,都会有用到轮播图的时候。 现在网上的轮播图插件也非常多,花样花式也各异,有基于jq的、基于框架,所以一般是不用我们去手写轮播图的。 但在某些情况下,我们还是需要去手写轮播图。 手写原生js轮播图,有助于我们知道轮播图的 ......

 

前言

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

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

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

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

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

 

效果

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

原生js轮播图原生js轮播图

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

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

 

原理

轮播图原理分析:

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

我们先来看一下html结构,注意力主要集中在画红圈的地方:

原生js轮播图

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

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

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

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

 

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

原生js轮播图

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

原生js轮播图

 

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

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

原生js轮播图

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

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

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

原生js轮播图

原生js轮播图

 

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

 

箭头点击

原生js轮播图

原生js轮播图(先看画红圈的地方,最后会给源码)

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

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

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

原生js轮播图(先看画红圈的地方,最后会给源码)

imgchange()函数的作用是:

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

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

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

 

按钮点击

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

1.切换到对应的图片;

2.改变按钮的样式。

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

原生js轮播图

原生js轮播图

原生js轮播图

主要看红圈内的代码,我们先完成第一件事:切换到对应的图片。

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

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

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

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

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

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

slide_index = data_index;

 

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

原生js轮播图

事先将选中按钮的样式 .active 写好:

原生js轮播图

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

原生js轮播图

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

原生js轮播图

 

滑动过渡

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

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

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

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

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

 

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

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

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

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

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

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

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

原生js轮播图

 

两张辅助图

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

原生js轮播图而不是原生js轮播图

 

自动播放

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

原生js轮播图

 

源码

原生js轮播图
<!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

 

优化

实现的轮播图并不是配置。

当我们改变图片的数量、width或height等时,都需要改动源码里多处地方,这样实在不方便。

至于如何优化就让大家自己去思考了。

 

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