p6rk9-9tpgf.gif

实现思路

左右运动版轮播图实现原理.png

HTML

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>左右版轮播图</title>
  8. <!-- IMPORT CSS -->
  9. <link rel="stylesheet" href="css/reset.min.css">
  10. <link rel="stylesheet" href="css/index.css">
  11. </head>
  12. <body>
  13. <div class="container" id="bannerBox">
  14. <!-- 轮播图 -->
  15. <div class="wrapper">
  16. <!-- <div class="slide"><img src="images/banner01.png" alt=""></div>
  17. <div class="slide"><img src="images/banner02.jpg" alt=""></div>
  18. <div class="slide"><img src="images/banner03.png" alt=""></div>
  19. <div class="slide"><img src="images/banner01.png" alt=""></div> -->
  20. </div>
  21. <!-- 分页器 -->
  22. <div class="pagination">
  23. <!-- <span index="0" class="active"></span>
  24. <span index="1"></span>
  25. <span index="2"></span> -->
  26. </div>
  27. <!-- 导航按钮 -->
  28. <div class="navigation prev"></div>
  29. <div class="navigation next"></div>
  30. </div>
  31. <!-- IMPORT JS -->
  32. <script src="js/index.js"></script>
  33. </body>
  34. </html>

CSS

.container{
    box-sizing: border-box;
    position: relative;
    margin: 20px auto;
    width: 1226px;
    height: 460px;
    overflow: hidden;
}
.container .wrapper{
    display: flex;
    /* justify-content: flex-start;
    align-items: flex-start; */
    position: absolute;
    top: 0;
    /* 默认展示第一张 */
    left: 0;
    /*width是根据slide数量决定的{不要忘记克隆的这一张}*/
    width: 400%; 
    height: 100%;
    /* 每一次wrapper左右运动,都是改变lefe值,我们让其具备过渡动画效果 */
    transition: left .3s;
}

.container .wrapper .slide{
    width: 1226px;
    height: 100%;
}

.container .wrapper .slide img{
    display: block;
    width: 100%;
    height: 100%;
}

/* 分页器 */
.container .pagination {
    display: flex;
    position: absolute;
    bottom: 20px;
    left: 50%;
    /* 居中 */
    transform: translateX(-50%);
    padding: 5px;
    /* 椭圆 */
    border-radius: 10px;
    background: rgba(0, 0, 0, .2);
}
.container .pagination span{
    margin: 0 8px;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #fff;
    cursor: pointer;
    transition: background .5s;
}

.container .pagination span.active{
    background:#aaa;
}

.container .navigation{
    position: absolute;
    top:50%;
    transform: translateY(-50%);
    width: 41px;
    height: 69px;
    /* 垂直居中 */
    background: url('../images/icon-slides.png') no-repeat;
    cursor: pointer;
    opacity: .5;

}

.container .navigation.prev {
    left: 0;
    background-position: 0 50%;
    cursor: pointer;
}

.container .navigation.next {
    right: 0;
    background-position: -42px 50%;
}

js

-1)基础实现版本

(function () {
    let bannerBox = document.querySelector('#bannerBox'), //最外层轮播图容器
        wrapper = bannerBox.querySelector('.wrapper'),//包裹所有图片的容器
        pagination = bannerBox.querySelector('.pagination'),//分页器容器
        paginationList = pagination.querySelectorAll('span');//分页器span标签集合

    // step记录当前展示这个slide的索引
    // autoTimer记录自动轮播的定时器
    // distance记录容器的宽度,也是每一次wrapper运动的距离  1226
    // count记录slide的总数量(包含克隆这一张)
    let step = 0,
        autoTimer = null,
        distance = bannerBox.offsetWidth,//容器的宽度
        count = 4;//包含克隆的哪一张

    // 实现自动轮播
    const autoMove = function autoMove() {
        step++;
        if (step >= count) {
            // 如果累加后的索引比最大索引都大,说明当前已经是末尾这一张(克隆):我们让其立即运动到真实第一张 & 再让其有动画效果的运动到第二张
            wrapper.style.transitionDuration = '0s';
            wrapper.style.left = '0px';
            step = 1;
            wrapper.offsetWidth;
        }
        wrapper.style.transitionDuration = '0.3s';
        wrapper.style.left = `${-step * distance}px`;
        // 每次切换完成,同时控制焦点对齐
        paginationFocus();
    };

    // 分页器对齐
    const paginationFocus = function paginationFocus() {
        let temp = step;
        if (temp === count - 1) temp = 0;
        paginationList.forEach((item, index) => {
            if (index === temp) {
                item.className = 'active';
                return;
            }
            item.className = '';
        });
    };



    // 基于事件委托实现容器中分页器点击切换&左右导航按钮左右切换
    bannerBox.addEventListener('click', function (ev) {
        let target = ev.target,
            tarTag = target.tagName;
        tarClass = target.className;
        // 点击分页器的焦点
        if (tarTag === "SPAN") {
            let index = +target.getAttribute('index');
            if ((index === step) || (step === count - 1 && index === 0)) return; //点击的这一项就是展示的这一项,则无需处理[不要忘记克隆的那个]{展示的克隆的那项 但是焦点处于0 所以也不用做处理}
            step = index;
            wrapper.style.left = `${-step * distance}px`;
            paginationFocus();//分页器焦点对齐
            return;
        }
        // 点击左右按钮
        if (tarTag === 'DIV' && tarClass.includes('navigation')) {
            // 左按钮
            if (tarClass.includes('prev')) {
                step--;
                if (step < 0) {
                    // 到达左边界:立即蹦到最后一张(克隆),然后运动到倒数第二张
                    wrapper.style.transitionDuration = '0s';
                    wrapper.style.left = `${-(count - 1) * distance}px`;
                    step = count - 2;
                    wrapper.offsetWidth;
                }
                wrapper.style.transitionDuration = '0.3s';
                wrapper.style.left = `${-step * distance}px`;
                paginationFocus();//分页器焦点对齐
                return;
            }
            // 右按钮 和自动轮播一样
            autoMove();
        }
    });

    // 开启自动轮播 & 控制自动轮播的暂停/继续
    autoTimer = setInterval(autoMove, 1000);//轮循定时器
    //当鼠标点上去要清空轮播图
    bannerBox.onmouseenter = () => clearInterval(autoTimer);
    //当鼠标离开的时候要 把轮播定时器加上
    bannerBox.onmouseleave = () => autoTimer = setInterval(autoMove, 1000);
})();

-2)最终实现版本

动态数据绑定&封装左右切换点击分页器切换方法

(function () {
    let bannerBox = document.querySelector('#bannerBox'),
        wrapper = bannerBox.querySelector('.wrapper'),
        pagination = bannerBox.querySelector('.pagination'),
        paginationList =null;
    // step记录当前展示这个slide的索引
    // autoTimer记录自动轮播的定时器
    // distance记录容器的宽度,也是每一次wrapper运动的距离  1226
    // count记录slide的总数量(包含克隆这一张)
    let step = 0,
        autoTimer = null,
        distance = bannerBox.offsetWidth,//容器的宽度
        count = 0;//包含克隆的哪一张

    // 获取数据&数据绑定
    const binding=function binding(){
        // 获取数据
        let data=[],
            str1=``;
            str2=``;
        let xhr=new XMLHttpRequest();
        xhr.open('GET','./data.json',false);
        xhr.onreadystatechange=function(){
            if(xhr.readyState===4&&xhr.status===200){
                data=JSON.parse(xhr.responseText);
            }
        };
        xhr.send();

        //数据绑定
        data.push(data[0]);//从数据层克隆一份到末尾
        data.forEach((item,index)=>{
            str1+=`<div class="slide"><img src="${item.pic}" alt=""></div>`;
            if(index<data.length-1){
                str2+=`<span index="${index}" class="${index===0?'active':''}" ></span>`;
            }
        });
        wrapper.innerHTML=str1;
        pagination.innerHTML=str2;

        count=data.length;
        wrapper.style.width=`${count*100}%`;
        paginationList=pagination.querySelectorAll('span');
    };

    //封装公共切换的方法
    const change = function change(dir) {
        if (dir != null) {
            if (dir === 'left') {
                //点击左按钮 切换到上一张
                step--;
                if (step < 0) {
                    // 到达左边界:立即蹦到最后一张(克隆),然后运动到倒数第二张
                    wrapper.style.transitionDuration = '0s';
                    wrapper.style.left = `${-(count - 1) * distance}px`;
                    step = count - 2;
                    wrapper.offsetWidth;
                }
            } else {
                //右按钮 切换到下一张
                step++;
                if (step >= count) {
                    // 如果累加后的索引比最大索引都大,说明当前已经是末尾这一张(克隆):我们让其立即运动到真实第一张 & 再让其有动画效果的运动到第二张
                    wrapper.style.transitionDuration = '0s';
                    wrapper.style.left = '0px';
                    step = 1;
                    wrapper.offsetWidth;
                }
            }
        }
        wrapper.style.transitionDuration = '0.3s';
        wrapper.style.left = `${-step * distance}px`;
        // 每次切换完成,同时控制焦点对齐
        paginationFocus();
    };



    // 分页器对齐
    const paginationFocus = function paginationFocus() {
        let temp = step;
        if (temp === count - 1) temp = 0;
        paginationList.forEach((item, index) => {
            if (index === temp) {
                item.className = 'active';
                return;
            }
            item.className = '';
        });
    };



    // 基于事件委托实现容器中分页器点击切换&左右导航按钮左右切换
    bannerBox.addEventListener('click', function (ev) {
        let target = ev.target,
            tarTag = target.tagName;
        tarClass = target.className;
        // 点击分页器的焦点
        if (tarTag === "SPAN") {
            let index = +target.getAttribute('index');
            if ((index === step) || (step === count - 1 && index === 0)) return; //点击的这一项就是展示的这一项,则无需处理[不要忘记克隆的那个]{展示的克隆的那项 但是焦点处于0 所以也不用做处理}
            step = index;
            change();
            return;
        }
        // 点击左右按钮
        if (tarTag === 'DIV' && tarClass.includes('navigation')) {
            // 左按钮
            if (tarClass.includes('prev')) {
                change('left');
                return;
            }
            // 右按钮 和自动轮播一样
            change('right');
        }
    });


    binding();
    // 开启自动轮播 & 控制自动轮播的暂停/继续
    autoTimer = setInterval(change.bind(null,'right'), 1000);//轮循定时器
    //当鼠标点上去要清空轮播图
    bannerBox.onmouseenter = () => clearInterval(autoTimer);
    //当鼠标离开的时候要 把轮播定时器加上
    bannerBox.onmouseleave = () => autoTimer = setInterval(change.bind(null,'right'), 1000);


})();