一个简单的纯js轮播图

本博客参照了此文章(https://www.cnblogs.com/LIUYANZUO/p/5679753.html)

效果如下:

1 自动播放

 

2 点击箭头切换

 

3 点击圆点切换


DOM

    <div id="container" class="container">
        <div class="pics">
            <img src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2193651974,4181473948&fm=26&gp=0.jpg" alt="">
            <img src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3057381651,2463402979&fm=26&gp=0.jpg" alt="">
            <img src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=379963434,28431380&fm=26&gp=0.jpg" alt="">
        </div>
        <div class="buttons">
            <div class="button on"></div>
            <div class="button"></div>
            <div class="button"></div>
        </div>
        <a href="javascript:;" id="prev" class="arrow">&lt;</a>
        <a href="javascript:;" id="next" class="arrow">&gt;</a>
    </div>

CSS

<style>
    img{
        height: 400px;
        width: 600px;
    }
    .container{
        position: relative;
        height: 400px;
        width: 600px;
        margin-right: 0;
        overflow: hidden;
        
    }
    .pics{
        z-index: 1;
        position: absolute;
        width: 1800px;
        height: 400px;
    }
    .pics img{
        float: left;
    }
    .arrow {
        position: absolute;
        z-index: 99;
        font-size: 36px;
        top: 180px;
        z-index: 2;
        display: none;
        font-weight: bold;
        line-height: 39px;
        text-align: center;
        width: 40px;
        height: 40px;
        color: #fff;
        background-color: RGBA(0, 0, 0, .3);
        cursor: pointer;
    }
    #prev {
        left: 10px;
    }
    #next {
        left: 550px;
    }
    .buttons{
        z-index: 99;
        position: absolute;
        bottom: 10px;
        left: 45%;
    }
    .button{
        display:inline-block;
        height: 20px;
        width: 20px;
        border-radius:50px;
        color:black;
        background:RGBA(0, 0, 0, .3);
    }
    .on{
        background: pink;
    }
</style>

JS

<script>
    const container = document.getElementById('container')
    const pics = document.getElementsByClassName('pics')[0];
    const prev = document.getElementById('prev');
    const next = document.getElementById('next');
    const buttons = document.getElementsByClassName('button');
    let index = 0;
    
    // button相关
    for (let index = 0; index < buttons.length; index++) {
        buttons[index].addEventListener('click', function(){buttonsShow(index)},false);    
    }
    function buttonsShow(_index = index) {
        for (var i = 0; i < buttons.length; i++) {
            if (Array.from(buttons[i].classList).includes('on')) {
                buttons[i].classList.remove('on');
            }
        }
        buttons[_index].classList.add('on');
        animate(_index*(-600), false);
    }

    //播放相关
    function play() {
        timer = setInterval(function(){
            // animate(-600, true);
            next.onclick();
        }, 2000);
    }
    function stop(){
        clearInterval(timer);
    }

    //动画相关
    function animate(offset, isRelative) {
        if (isRelative) {
            if (!pics.style.left) {
                var oldoffset = 0;
                var newoffset = oldoffset + offset;
            } else {
                var newoffset = parseInt(pics.style.left) + offset;
            }
            pics.style.left = newoffset + 'px';

            if (newoffset == 600) {
                pics.style.left = -1200 + 'px';
            }
            if (newoffset == -1800) {
                pics.style.left = 0 + 'px';
            }
        } else {         
            pics.style.left = offset + 'px';            
        }
    }

    //前进后退键相关
    prev.onclick = function() {
        animate(600, true);
        if(index == 0) {
            index = 2;
        } else {
            index = index - 1;
        }
        buttonsShow();
    }
    next.onclick = function() {
        animate(-600, true);
        if(index == 2) {
            index = 0;
        } else {
            index = index + 1;
        }
        buttonsShow();
    }  
    container.addEventListener('mouseover',function () {
        console.log('in');
        prev.style.display = 'block';
        next.style.display = 'block';
        stop();
    },true)

    container.addEventListener('mouseout',function () {
        prev.style.display = 'none';
        next.style.display = 'none';
        play();
    },true)
    
    play();
    


    // container.onmouseover = function(){
    //     stop();
    // };
    // // container.onmouseover = stop;或者这么写也可以!
    // container.onmouseleave = function(){
    //     play();
    // }

    // registerEvent(container, 'onmousemove');
    // registerEvent(container, 'onmouseover');
    // function registerEvent(target, event) {
    //     target.addEventListener(event, function () {
    //     // console.log('ss');
    //     prev.style.display = 'block';
    //     next.style.display = 'block';
    // }, false)} 
</script>

所遇到的坑:


1.方法的声明和调用


     1⃣️ 一开始我是这么写的,但是发现点击前进后退按钮没有效果,在annimate函数里面console.log之后发现只有刷新打开页面的时候打印了,之后的点击并没有打印,说明没有进animate函数里面。

  prev.onclick =  animate(600);
  next.onclick = animate(-600);

     2⃣️ 修改成下面代码这样之后就可以了。我这个问题错在混淆了声明和调用的定义,onclick后面要赋值的是一个函数,当click的时候我们会去调用这个函数,而不是赋值一个函数结果!!

      js文件在加载的时候,会自顶向下自动运行一遍,如果如上代码一样,prev.onclick得到的是animate函数执行的结果,也就是null;修改之后,我们吧animate放在一个函数里面,这个函数的执行结果是animate函数,这样我们onclick才能正常调用该函数。

    prev.onclick = function() {
        animate(600);
    }
    next.onclick = function() {
        animate(-600);
    }

     3⃣️ 还有第三种写法,并非一定要用一个function包起来,我们可以把不带括号的animate赋给onclick事件,这样onclick得到的是animate这个函数,而不是执行结果,因为‘()‘表示执行!,但是这样有个弊端,就是不能传参。

prev.onclick = animate;
next.onclick = animate;

2. 块级作用域和闭包

    for (var i = 0; i < buttons.length; i++) {
        buttons[i].addEventListener('click', function(){buttonsShow(i)},false);    
    }

      上面这段代码的目的是给每个小圆点添加一个click的监听,点击之后能够切换到相应的图片。但是我们发现,这么写点击之后没有任何效果。我们在buttonShow函数里面添加一个console,打印传过来的值,结果发现不管点击哪个,打印出的值永远是3.

      原因是这样的,ES5里面是没有块级作用域的,在高级程序设计第三版中76页,会看到这么一句话:

            “对javascript来说,由for语句创建的变量i即使在for循环执行结束后,也依旧会存在于循环外部的执行环境中。”

     因为我们在for循环里面定义的计数参数i,因为没有块级作用域,所以是一个全局变量,说明它并不会消失且保持最后的执行结果,我这里又3张图片,所以按理来说i=2,但是因为最后一次执行完之后还会执行一次i++,所以i最后在全局保持值为3。所以!!三个小圆点不管如何点击,传递的参数始终为3。(还不明白同学建议看一下高级程序设计3 第七章)。

      ps:报错是因为最后一次执行的i++让i最后加到了3,超出了图片的index上限-----2,所以报错。

    1⃣️这种问题相信大家遇到过很多次,以前ES6没有出来的时候,只有一种解决办法,那就是立即执行函数仿块级作用域。如下所示。

(function(){
    ......
})();

      之前看高级程序三,感觉解释的并不是特别清楚。我对仿块级作用域的理解是这样的,虽然像if for循环这样的语句没有块级作用域,但是函数是有块级作用域的,在函数里面定义的变量,在函数外取不到并且函数执行完毕之后,变量会被销毁,仿块级作用域正是利用了这个特点来模仿 块级作用域。

      我们把需要块级的地方用匿名函数进行模拟,并添加一对圆括号,对该函数进行立即执行,这样保证了变量能够及时销毁。加圆括号同时也是因为 如果单单只是写一个没有名字的匿名函数,浏览器会认为你在申明一个函数,但是这个函数却没有名字,因此会报错。

通过仿块级作用域,代码修改如下:

    for (var i = 0; i < buttons.length; i++) {
         // 这里使用的是立即执行函数,
         (function(i) {
             buttons[i].addEventListener('click', function(){buttonsShow(i)},false);
          })(i)
        }

2⃣️ES6出来之后,想想其最著名的一条是什么?那就是块级作用域!

      我们单单只需要把计数参数的申明var变成let即可!!这样for循环里面就是一个块级作用域!多么简单啊,不能想象没有ES6的生活。。。。

    for (let i = 0; i < buttons.length; i++) {
        buttons[i].addEventListener('click', function(){buttonsShow(i)},false);    
    }

3.事件冒泡和事件捕获

 

小心得

1.函数默认参数

    function buttonsShow(_index = index) {
        console.log(_index);
        
        for (let i = 0; i < buttons.length; i++) {
            if (Array.from(buttons[i].classList).includes('on')) {
                buttons[i].classList.remove('on');
            }
        }
        buttons[_index].classList.add('on');
        animate(_index*(-600), false);
 
 

      buttonShow函数有两处地方用到,一是轮播/点击前进后退的时候,二是点击确定小圆点的时候;这两种情况肯定都要向函数传index才能改变相应的小圆点的样式。但是第一种情况的index是不确定的,是一个相对值不是一个绝对值,每次要么+1,要么-1;第二种情况点击小圆点得到的index是一个绝对值index,我们能知道具体点击的是第几个小圆点。

      我的解决办法是全局设置一个index,在左右切换的时候对这个全局index进行改变,这样我们也无需给buttonShow传值了,因为index是全局的,所有函数都可以调用。然而这样会有个小毛病,那就是buttonShow函数怎么知道是使用第一种情况的全局index还是使用第二种情况传入的index?

      这里用到了函数的默认参数,因为两种情况一个传值了一个没有传值,所以第一种情况没有传值的时候就用默认参数---全局的index,第二种情况传值具体的index就把默认参数给覆盖了。

2.函数复用/功能抽离

      相似的,

      Animate函数目的是用于改变小圆点的样式。逻辑里面一共有两处需要用到这个函数,第一个是点击确定小圆点切换图片,第二个是自动播放和点击前进后退按钮时候切换图片。

      一开始我想到都是同样的功能,那就统一使用animate函数比较好,但是这两处有一点点不一样。第一处是点击确定的小圆点,得到一个绝对值index;第二处是每次给pics左右加减600px来切换图片,给animate的传参是一个相对值,不是一个绝对值。这样导致一个比较尴尬的事情,animate函数一开始是设定为接受相对值的

     那我们点击小圆点得到的绝对值index怎么转换为相对值?

     我的解决办法是,给aniamte函数多加一个参数isRelative,true的情况就是简单的轮播/点击前进后退,第一个参数offset传如的是一个相对值600px;false就是点击小圆点切换图片情况,第一个参数offset传入的是图片的绝对定位值。

    function animate(offset, isRelative) {
        if (isRelative) {
            if (!pics.style.left) {
                var oldoffset = 0;
                var newoffset = oldoffset + offset;
            } else {
                var newoffset = parseInt(pics.style.left) + offset;
            }
            pics.style.left = newoffset + 'px';

            if (newoffset == 600) {
                pics.style.left = -1200 + 'px';
            }
            if (newoffset == -1800) {
                pics.style.left = 0 + 'px';
            }
        } else {         
            pics.style.left = offset + 'px';            
        }
    }
    for (var i = 0; i < buttons.length; i++) {
        buttons[i].addEventListener('click', function(){buttonsShow(i)},false);    
    }
    function buttonsShow(_index = index) {
        console.log(_index);
        
        for (let i = 0; i < buttons.length; i++) {
            if (Array.from(buttons[i].classList).includes('on')) {
                buttons[i].classList.remove('on');
            }
        }
        buttons[_index].classList.add('on');
        animate(_index*(-600), false);

3.

注意一下,next.onclick()这个用法。

    function play() {
        timer = setInterval(function(){
            // animate(-600, true);
            next.onclick();
        }, 2000);
    }

 

 

 

 

 

 

 

Log in

to use Ginger

Limited mode

;}

×

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值