小米官网过渡式轮播图

如果想看最优解,请跳到Version 2

Version 1

第一次自己尝试轮播图,思路大概是这样的:

点击按钮切换图片&导航点→点击导航点切换图片→自动播放图片并切换导航点→添加过渡效果

其中,切换图片用的是修改src的方式,导致我在添加过渡时绕了弯路,最后的效果差强人意,这只是过渡轮播图的第一版。

后来又尝试了把图片横着排,改变left的第二版,意识到官网的过渡效果应该同时包括淡入和淡出,即上一张图片淡出的时候下一张图片淡入,我前两次的尝试都是把每张图片分开的,不论是修改src还是修改left,两张图片没有交集,自然做不出好的过渡效果。
HTML

    <div class="banner-wrapper">
          <div class="banner w">
              <ul class="img">
                    <li><a href="#"><img class="banner-img"src="./img/banner1.jpg"></a></li>
              </ul>
              <div class="dot-wrapper">
                  <a href="#" class="dot"></a>
                  <a href="#" class="dot"></a>
                  <a href="#" class="dot"></a>
                  <a href="#" class="dot"></a>
                  <a href="#" class="dot"></a>
              </div>
              <div>
                  <a href="#" class="prev"></a>
              </div>
              <div>
                  <a href="#" class="next"></a>
              </div>
          </div>
    </div>

CSS

.banner{
    position: relative;
    height: 460px;
}
.banner .img li{
    position: absolute;
}
.banner .banner-img{
    width: 100%;
}
.transition{
    width: 100%;
    animation: Ani 800ms linear 1;
}
@keyframes Ani{
    0%{
        opacity: 0.4;
    }
    100%{
        opacity: 1;
    }
}
.dot-wrapper{
    width: 80px;
    height: 10px;
    bottom: 22px;
    right: 35px;
    position: absolute;
}
.dot{
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background-color: rgba(0, 0, 0, .4);
    border: 2px rgba(255, 255, 255, .4) solid;
    margin-left: 6px;
    float: left;
}
.active{
    border-color: rgba(0, 0, 0, .4);
    background-color:rgba(255, 255, 255, .4);    
}
.dot:hover{
    border-color: rgba(0, 0, 0, .4);
    background-color:rgba(255, 255, 255, .4);
}
.prev,.next{
    display: block;
    position: absolute;
    width: 41px;
    height: 69px;
    background-image: url(../img/icon-slides.png);
    top: 184px;
}
.prev{
    left: 233px;
    background-position: -83px;
}
.prev:hover{
    background-position: 0px;
}
.next{
    right: 0;
    background-position: -124px;
}
.next:hover{
    background-position: -41px;
}

JS

window.onload = function(){
    let prev = document.getElementsByClassName("prev")[0];
    let next = document.getElementsByClassName("next")[0];
    let img = document.getElementsByClassName("banner-img")[0];
    let dots = document.getElementsByClassName("dot");
    let imgArr = ["img/banner1.jpg","img/banner2.jpg","img/banner3.jpg","img/banner4.jpg","img/banner5.jpg"];
    let index = 0;
    //let count = 0; 动画播放次数计时器
    dots[index].className = "dot active"//让第一个导航点选中
    //点击按钮切换图片,改变导航点,设置过渡
    prev.onclick = function(){
        index--;
        // count++;
        if(index < 0){
            index = imgArr.length - 1;
        }
        // if(img.className == "transition2" || img.className == "banner-img"){
        //     img.className = "transition1";
        // }
        // if(img.className == "transition1" || img.className == "banner-img"){
        //     img.className = "transition2";
        // }
        //尝试双类名交替,让动画多次播放,失败

        //img.className = "transition";
        //img.style.animationIterationCount = count;
        //尝试增加播放次数,让动画多次播放,失败

        img.className = "transition";//这一句必需,否则transition函数的第一句无法发挥作用,达不到预期效果
        transition();//必须在第一句的下面

        img.src = imgArr[index];
        dots[index].className = "dot active";//让当前导航点选中
        if(index+1 <= dots.length-1){
            dots[index+1].className = "dot";
        }
        else{
            dots[0].className = "dot";
        }
        clearInterval(timer);//关闭之前的定时器(每次点击按钮时要重新开始计时)
        timer = autoRun();//设置新的定时器
    }

    next.onclick = function(){
        index++;
        if(index > imgArr.length -1){
            index = 0;
        }
        img.className = "transition";
        transition();
        img.src = imgArr[index];
        dots[index].className = "dot active";//让当前导航点选中
        if(index-1 >= 0){
            dots[index-1].className = "dot"
        }
        else{
            dots[dots.length-1].className = "dot"
        }
        clearInterval(timer);//关闭之前的定时器
        timer = autoRun();//设置新的定时器
    }
    //点击导航点切换图片
    let tmp = 0;
    for(let i = 0;i < dots.length; i++){
        dots[i].onclick = function(){
            dots[index].className = "dot";
            //双变量方法:index是点击prev和next对应的图片编号,i是属于每个导航点的编号
            //不能在2个for循环内都用index,否则无法修改index,即最后一行的代码无法实现

            //先让index对应的导航点未选中
            dots[tmp].className = "dot";
            //tmp表示之前 通过点击导航点 而选中的导航点,让它未选中
            dots[i].className = "dot active";
            //让 最新一次点击的导航点 选中
            img.src = imgArr[i];
            img.className = "transition";
            transition();
            //改变图片
            tmp = i;
            //更新tmp
            index = i;
            //修改index,否则再次点击prev或next时会出问题

            clearInterval(timer);//关闭之前的定时器
            timer = autoRun();//设置新的定时器
        }
    }
    timer = autoRun();

    img.style.transitionDuration = "1s";
    //图片的自动轮播函数
    function autoRun(){
        let timer = setInterval(function(){
            index++;
            if(index >= imgArr.length){
                index = 0;
                dots[dots.length-1].className = "dot";
            }
            else{
                dots[index-1].className = "dot";
            }
            img.src = imgArr[index];
            img.className = "transition";
            transition();
            dots[index].className = "dot active";
        },5000);
        // let timer_ = transition();
        // img.style.opacity = 1;
        return timer;
        //timer封装在autoRun函数内,外层访问不到,必须返回timer,这样才能关闭定时器
    }

    //轮播的过渡效果
    function transition(){
        // img.style.opacity = 1;   默认值
        // let timer_ = setTimeout(() => {
        //     img.style.opacity = 0;
        // }, 4000);
        // img.style.opacity = 1;
        //如果像上边这样写,会先执行最后一行代码,再执行定时器,错误
        //setTimeOut定时器不可行,因为在点击按钮和导航点时是等不到4s的,会失去过渡效果,而且定时器的开关问题比较麻烦

        document.querySelector(".transition").className = "banner-img";
        //注意,需要先在prev,next和导航点的onclick回调函数中,将img的className设为transition
        window.requestAnimationFrame(function(time){
            window.requestAnimationFrame(function(time){
                document.querySelector(".banner-img").className = "transition";
            });
        });
    }
    //如果不在上方直接调用transition函数,写成这种绑定的形式也可以:
    // document.querySelector(".prev").addEventListener("click", transition, false);
    // document.querySelector(".next").addEventListener("click", transition, false);
    // for(let j = 0;j < dots.length;j++){
    //     dots[j].addEventListener("click", transition, false);
    // }
}

最后过渡的效果,是反复执行一段动画(半透明→不透明)实现的,至于如何反复执行一段动画,参考了CSS动画之animation再次触发与防止多次触发

Version 2

后来上网查,发现正确做法应该是:把所有图片叠在一起,每次只有一张的opacity值为1,其它为0,切换图片时候就把要隐藏元素的opacity设为0,要显示的设为1,再加上transition过渡,大功告成

HTML

    <div class="banner-wrapper">
          <div class="banner w">
              <ul class="img-list">
                    <li class="banner-img no-trans"><a href="#"><img src="./img/banner1.jpg"></a></li>
                    <li class="banner-img"><a href="#"><img src="./img/banner2.jpg"></a></li>
                    <li class="banner-img"><a href="#"><img src="./img/banner3.jpg"></a></li>
                    <li class="banner-img"><a href="#"><img src="./img/banner4.jpg"></a></li>
                    <li class="banner-img"><a href="#"><img src="./img/banner5.jpg"></a></li>
              </ul>
              <div class="dot-wrapper">
                  <a href="#" class="dot"></a>
                  <a href="#" class="dot"></a>
                  <a href="#" class="dot"></a>
                  <a href="#" class="dot"></a>
                  <a href="#" class="dot"></a>
              </div>
              <div>
                  <a href="#" class="prev"></a>
              </div>
              <div>
                  <a href="#" class="next"></a>
              </div>
          </div>
    </div>

CSS

.banner{
    position: relative;
    height: 460px;
} 
.banner li{
    position: absolute;
    opacity: 0;
    transition: opacity 800ms;
}
.banner li.no-trans{
    opacity: 1;
    /*让第一张图片设为不透明,避免刷新页面时多余的过渡*/
    /*需要提高该选择器的优先级*/
}
.banner img{
    width: 100%;
}
.dot-wrapper{
    width: 80px;
    height: 10px;
    bottom: 22px;
    right: 35px;
    position: absolute;
}
.dot{
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background-color: rgba(0, 0, 0, .4);
    border: 2px rgba(255, 255, 255, .4) solid;
    margin-left: 6px;
    float: left;
}
.active{
    border-color: rgba(0, 0, 0, .4);
    background-color:rgba(255, 255, 255, .4);    
}
.dot:hover{
    border-color: rgba(0, 0, 0, .4);
    background-color:rgba(255, 255, 255, .4);
}
.prev,.next{
    display: block;
    position: absolute;
    width: 41px;
    height: 69px;
    background-image: url(../img/icon-slides.png);
    top: 184px;
}
.prev{
    left: 233px;
    background-position: -83px;
}
.prev:hover{
    background-position: 0px;
}
.next{
    right: 0;
    background-position: -124px;
}
.next:hover{
    background-position: -41px;
}

JS

window.onload = function(){
    let prev = document.getElementsByClassName("prev")[0];
    let next = document.getElementsByClassName("next")[0];
    let imgs = document.getElementsByClassName("banner-img");
    let dots = document.getElementsByClassName("dot");
    let index = 0;
    imgs[index].style.opacity = 1;
    dots[index].className = "dot active"
    prev.onclick = function(){
        index--;
        if(index >= 0){
            imgs[index].style.opacity = 1;
            imgs[index + 1].style.opacity = 0;
            dots[index].className = "dot active";
            dots[index + 1].className = "dot";]
        else{
            index = imgs.length - 1;
            imgs[index].style.opacity = 1;
            imgs[0].style.opacity = 0;
            dots[0].className = "dot";
            dots[dots.length - 1].className = "dot active";
        }
        clearInterval(timer);
        timer = autoRun();

    }
    next.onclick = function(){
        index++;
        if(index < imgs.length){
            imgs[index - 1].style.opacity = 0;
            imgs[index].style.opacity = 1;
            dots[index].className = "dot active";
            dots[index - 1].className = "dot";
        }
        else{
            index = 0;
            imgs[imgs.length - 1].style.opacity = 0;
            imgs[index].style.opacity = 1;
            dots[dots.length - 1].className = "dot";
            dots[0].className = "dot active";
        }
        clearInterval(timer);
        timer = autoRun();
    }
    for(let i = 0; i < dots.length; i++){
        dots[i].onclick = function(){
            imgs[index].style.opacity = 0;
            dots[index].className = "dot";
            imgs[i].style.opacity = 1;
            dots[i].className = "dot active";
            index = i;
            clearInterval(timer);
            timer = autoRun();
        }
    }
    function autoRun(){
        let timer = setInterval(function(){
            index++;
            if(index < imgs.length){
                imgs[index - 1].style.opacity = 0;
                imgs[index].style.opacity = 1;
                dots[index].className = "dot active";
                dots[index - 1].className = "dot";
            }
            else{
                index = 0;
                imgs[imgs.length - 1].style.opacity = 0;
                imgs[index].style.opacity = 1;
                dots[dots.length - 1].className = "dot";
                dots[0].className = "dot active";
            }
        },5000);
        return timer;
    }
    let timer = autoRun();
}

Version2基本上就能还原小米官网的轮播图了

如果有帮助的话,请帮我Star一下→谢谢大佬动动手指

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值