美文网首页
用状态机写轮播

用状态机写轮播

作者: astak3 | 来源:发表于2018-08-07 14:46 被阅读0次

    刚刚的轮播用具体思维做,因为不知道它有哪几种状态,就一步步来做,等效果做出来后,哪几种状态,一目了然。下面就用抽象思维做一遍.

    用抽象思维做

    初始化CSS样式

    *{
        margin:0;
        padding:0;
        box-sizing:border-box;
    }
    .window{
        width:400px;
        height:300px;
        overflow:hidden;
        margin:20px auto
    }
    .images{
        position:relative;
    }
    .images > img{
        position:absolute;
        transition:all 0.5s;
        width:100%;
        top:0;
    }
    

    轮播状态

    先来看下这个轮播有那几种状态

    1. 图片出现在视窗状态,我用 current 表示
    2. 图片离开视窗状态,我用 leave 表示
    3. 图片准备进入视窗状态,我用 enter 表示
      现在就是要写三个类,通过JS 激活class 来实现轮播
    .images > img.current{
        transform:translateX(0);
        z-index:1;
    }
    .images > img.leave{
        transform:translateX(-100%);
        z-index:1;
    }
    .images > img.enter{
        transform:translateX(100%);
    }
    

    梳理下每张图片的状态

    1. 初始化每张图片的位置,图片1 出现在当前视窗current,图片2、图片3 应该在视窗右边待命,随时准备进入视窗enter
    2. 当图片1 离开视窗时leave,图片2 进入视窗current
    3. 当上一步全部完成后,图片1 应该进入右边待命,等待着进入视窗
    4. 这里主要绝对定位后,会触发BFC
    $('#images > img:nth-child(1)').addClass('current');
    $('#images > img:nth-child(2)').addClass('enter');
    $('#images > img:nth-child(3)').addClass('enter');
    
    setTimeout(function(){
        $('#images > img:nth-child(1)').removeClass('current').addClass('leave').one('transitionend',function(e){
            $(e.currentTarget).addClass('enter').removeClass('leave')
        });
        $('#images > img:nth-child(2)').removeClass('enter').addClass('current')
    },3000);
    
    setTimeout(function(){
        $('#images > img:nth-child(2)').removeClass('current').addClass('leave').one('transitionend',function(e){
            $(e.currentTarget).addClass('enter').removeClass('leave')
        });
        $('#images > img:nth-child(3)').removeClass('enter').addClass('current')
    },6000);
    
    setTimeout(function(){
        $('#images > img:nth-child(3)').removeClass('current').addClass('leave').one('transitionend',function(e){
            $(e.currentTarget).addClass('enter').removeClass('leave')
        });
        $('#images > img:nth-child(1)').removeClass('enter').addClass('current')
    },9000);
    

    这样一轮循环就结束了,可以在往后添加setTimeout方法。

    无限循环下去

    大量重复的代码就需要寻找合适的的API 代替,一直播下去我们可以使用DOM APIsetInterval()

    $('#images > img:nth-child(1)').addClass('current');
    $('#images > img:nth-child(2)').addClass('enter');
    $('#images > img:nth-child(3)').addClass('enter');
    
    let n = 1;
    setInterval(function(){
        $(`#images > img:nth-child(${x(n)})`).removeClass('current').addClass('leave').one('transitionend',function(e){
            $(e.currentTarget).addClass('enter').removeClass('leave')
        });
        $(`#images > img:nth-child(${x(n+1)})`).removeClass('enter').addClass('current')
        
        n++;    //这里n 是自然增长,让它一直玄幻下去
    },3000)
    
    //n取值应该是[1,2,3,4,5,...,size]
    let allImages = $('#images > img');
    let size = allImages.length;
    function x(n){
        if(n > size){   //如果n 大于节点size,n就取余
            n = n%size; 
            if(n === 0){    //如果n 取余为0,则让n等于size
                n = size;
            }
        }
        return n;
    }
    

    这样就是实现了无缝轮播,上面用到了ES6的插值法。
    在CSS中img:nth-child(n)是没有这种写法的,它不能像JS一样可以用变量,这边就用到了ES6 的插值法
    `img:nth-child(${n})`\

    最后优化下刚刚写的代码

    <style>
        *{
            margin:0;
            padding:0;
            box-sizing:border-box;
        }
        .window{
            width:400px;
            height:300px;
            overflow:hidden;
            margin:20px auto
        }
        .images{
            position:relative;
        }
        .images > img{
            position:absolute;
            transition:all 0.5s;
            width:100%;
            top:0;
        }
    <style>
    
    <div class="window">
        <div id="images" class="images">
            <img class='png1' src="./images/1.png" width=400 alt="">
            <img class='png2' src="./images/2.png" width=400 alt="">
            <img class='png3' src="./images/3.png" width=400 alt="">
            <img class='png4' src="./images/4.png" width=400 alt="">
            <img class='png5' src="./images/5.png" width=400 alt="">
        </div>
    </div>
    
    <script>
        let n = 1;
        int();
        setInterval(function(){
            makeLeave(getImage(n)).one('transitionend',function(e){
                makeEnter($(e.currentTarget))
            });
            makeCurrent(getImage(n+1));
            n++;    //这里n 是自然增长,让它一直循环下去
        },1000);
        
        //n取值应该是[1,2,3,4,5,...,size]
        let allImages = $('#images > img');
        let size = allImages.length;
        function x(n){
            if(n > size){   //如果n 大于节点size,n就取余
                n = n%size;
                if(n === 0){    //如果n 取余为0,则让n等于size
                    n = size;
                }
            }
            return n;
        }
        
        function getImage(n){
            return $(`#images > img:nth-child(${x(n)})`)
        }
        
        function int(){
            $(`#images > img:nth-child(${n})`).addClass('current').siblings().addClass('enter');
        }
        
        function makeLeave($node){
            return $node.removeClass('current enter').addClass('leave')
        }
        function makeCurrent($node){
            return $node.removeClass('enter leave').addClass('current')
        }
        function makeEnter($node){
            return $node.addClass('enter').removeClass('leave current')
        }
    </script>
    

    优化完了之后,实际代码就只有这么多,这个被称为状态机,现在再看轮播后,脑海里已经自动变成了状态机了。

    let n = 1;
    int();
    setInterval(function(){
        makeLeave(getImage(n)).one('transitionend',function(e){
            makeEnter($(e.currentTarget))
        });
        makeCurrent(getImage(n+1));
        n++;    //这里n 是自然增长,让它一直循环下去
    },1000);
    

    这里我遇到一个最大的问题之前,是用setTimeout()方法写的代码,后面做无限循环时没想到用setInterval()方法,怎么调试都不对,这里看下它们两个的区别:
    setTimeout()方法设置一个定时器,在到时间后执行一段代码或者函数
    setInterval()方法是重复调用一段代码或者函数,每次调用之间有固定的时间延时
    我们上面写在setInterval()方法内的函数其实就是一段固定的代码,每个一段时间执行一次,就变成我们看到的轮播了

    总结

    这一篇的核心是状态机,把动作变成一个个状态。用具体化写出的代码都是执行的动作,而用抽象化写出的代码都是完成后的状态,代码结构更清晰,更美观。当然要能写抽象化的代码,肯定少不了具体化的思维。

    用这种方法最大的好处是行为样式分离,如果我要给变轮播的方向,只需要改变CSS中的移动方向即可,还可以根据需要加上一些酷炫的操作。

    相关文章

      网友评论

          本文标题:用状态机写轮播

          本文链接:https://www.haomeiwen.com/subject/mhfavftx.html