实现思路

3D轮播图实现的思路.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>3D版轮播</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. <header class="headerBox">
  14. <div class="container" id="container">
  15. <!-- 轮播图 -->
  16. <div class="wrapper">
  17. <!-- <div class="slide active">
  18. <img src="images/01.jpg" alt="">
  19. <div class="mark"></div>
  20. <div class="desc">
  21. <p>蒙奇·D·路飞</p>
  22. <p>身份:草帽海贼团船长</p>
  23. <p>梦想:找到ONE PIECE,并成为海贼王</p>
  24. </div>
  25. </div> -->
  26. </div>
  27. <!-- 导航安钮 -->
  28. <div class="navigation prev"></div>
  29. <div class="navigation next"></div>
  30. </div>
  31. </header>
  32. <!-- IMPORT JS -->
  33. <script src="js/index.js" defer></script>
  34. </body>
  35. </html>

CSS

.headerBox{
    height: 350px;
    background: url('../images/bg.jpg') no-repeat;
    background-size: cover;
    position: relative;
}

.container{
    position:absolute;
    top:50%;
    left: 50%;
    transform: translate(-50%,-50%);
    width:1100px;
    height:300px;
}

.container .wrapper{
    position: relative;
    height: 100%;
}

.container .wrapper .slide{
    position:absolute;
    top:50%;
    left: 50%;
    transform: translate(-50%,-50%);
    z-index: 0;
    box-sizing: border-box;
    width: 25%;
    height: 100%;
    border: 3px solid #000;
    overflow: hidden;
    /* 以后修改slide样式,执行css3过渡动画 */
    transition: transform 0.5s;
    cursor: pointer;
}

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

.container .wrapper .slide .mark{
    position: absolute;
    top:0;
    left:0;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,.75);
    transition: background .5s;
}

.container .wrapper .slide.active .mark,
.container .wrapper .slide:hover .mark{
    background: rgba(0,0,0,0);
}

.container .wrapper .slide .desc{
    position: absolute;
    left:0;
    bottom: 0;
    box-sizing: border-box;
    padding: 10px;
    width: 100%;
    height: 40%;
    background: rgba(0,0,0,.75);
    /* 渐变 */
    background: -webkit-linear-gradient(top,rgba(0,0,0,.35),rgba(0,0,0,.75));
    /* 开始不显示 */
    transform:translateY(100%);
    transition:transform .3s;
    overflow: hidden;
}

.container .wrapper .slide.active:hover .desc{
    transform:translateY(0);
}

.container .wrapper .slide .desc p{
    line-height: 2;
    color:#fff;
    font-size: 12px;
}


/* 按钮 */

.container .navigation{
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 39px;
    height: 80px;
    background: url('../images/btn.png') no-repeat;
    display: none;
    cursor: pointer;
}

.container .navigation.prev{
    left:0;
    background-position: 0 0;
}
.container .navigation.next{
    right:0;
    background-position: -39px 0;
}

JS

(function () {
    // 获取元素
    let container = document.querySelector('#container'), //最外层轮播图容器
        wrapper = container.querySelector('.wrapper'),//包裹所有图片的容器
        slides = null,//所有图片的集合
        navprev = container.querySelector('.navigation.prev'),//点击上一个
        navnext = container.querySelector('.navigation.next'); //点击下一个
    let step = 0, //当前显示
        atuoTimer = null,//存放定时器
        count = 0,//一共多少个
        interval = 1000,//定时器的时间
        data = [];//存放的数据

    // 获取数据
    const queryData = function queryData() {
        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();
    };

    // 实现数据绑定
    //initial 初始值
    const binding = function binding(initial) {
        //1.如果数据不足5条,我们需要补齐5条

        if (data.length === 0) return;
        while (data.length < 5) {
            let diff = 5 - data.length,
                clone = data.slice(0, diff);
            data = data.concat(clone);
        }

        count = data.length;

        // 2.在绑定slide之前 需要按照规则给每个slide设置的样式 增加到数据的每一项中[z-index,className,sty{transform}]
        let temp1 = step - 2,
            temp2 = step - 1,
            temp3 = step, //正中间的那个
            temp4 = step + 1,
            temp5 = step + 2;
        if (temp1 < 0) temp1 = count + temp1;
        if (temp2 < 0) temp2 = count + temp2;
        if (temp4 > count - 1) temp4 = temp4 - count;
        if (temp5 > count - 1) temp5 = temp5 - count;

        data = data.map((item, index) => {
            let zIndex = 0,
                className = 'slide',
                transform = 'translate(-50%,-50%) scale(0.55)';
            switch (index) {
                case temp1:
                    zIndex = 1;
                    transform = 'translate(-195%,-50%) scale(0.7)';
                    break;
                case temp2:
                    zIndex = 2;
                    transform = 'translate(-130%,-50%) scale(0.85)';
                    break;
                case temp3:
                    className = 'slide active',
                        zIndex = 3;
                    transform = 'translate(-50%,-50%) scale(1)';
                    break;
                case temp4:
                    zIndex = 2;
                    transform = 'translate(30%,-50%) scale(0.85)';
                    break;
                case temp5:
                    zIndex = 1;
                    transform = 'translate(95%,-50%) scale(0.7)';
                    break;
            }
            item.className = className;
            item.sty = `z-index:${zIndex};transform:${transform};`;
            return item;
        });

        //5.如果initial不是true,说明不是第一次执行这个方法,此时我们按照最新计算的样式,修改每个slide样式即可
        if (!initial) {
            data.forEach((item, index) => {
                let {
                    className,
                    sty
                } = item;
                slides[index].className = className;
                slides[index].style.cssText = sty;//cssText
            })
            return;
        }

        //3.数据渲染,动态创建slide 并且设置样式
        let str = ``;
        data.forEach(item => {
            let { pic, className, sty, descript: { name, identity, dream } } = item;
            str += `<div class="${className}" style="${sty}">
                    <img src="${pic}" alt="">
                    <div class="mark"></div>
                    <div class="desc">
                        <p>${name}</p>
                        <p>身份:${identity}</p>
                        <p>梦想:${dream}</p>
                    </div>
                </div> `;
        });
        wrapper.innerHTML = str;


        //4.获取slide&控制按钮显示
        slides = wrapper.querySelectorAll('.slide'),
            navprev.style.display = navnext.style.display = 'block';
    };


    // 自动轮播
    const autoMove = function autoMove() {
        step++;
        if (step >= count) step = 0;
        binding();
    };


    // 控制自行轮播暂停或者开启
    container.addEventListener('mouseenter', () => clearInterval(atuoTimer))
    container.addEventListener('mouseleave', () => atuoTimer = setInterval(autoMove, interval));

    // 点击左右按钮切换
    container.addEventListener('click', function (ev) {
        let target = ev.target,
            targetTag = target.tagName,
            targetClass = target.className;
        if (targetTag === 'DIV' && targetClass.includes('navigation')) {
            if (targetClass.includes('prev')) {
                // 点击的是左按钮
                step--;
                if (step < 0) step = count - 1;
                binding();
                return;
            }
            //右按钮和自动轮播一样
            autoMove();
        };

    });

    queryData();
    binding(true);//绑定数据
    atuoTimer = setInterval(autoMove, interval); //设置轮循定时器

})();

JSON

[{
    "id": 1,
    "pic": "images/01.jpg",
    "descript": {
        "name": "蒙奇·D·路飞",
        "identity": "草帽海贼团船长",
        "dream": "找到ONE PIECE,并成为海贼王"
    }
}, {
    "id": 2,
    "pic": "images/02.jpg",
    "descript": {
        "name": "罗罗诺亚·索隆",
        "identity": "草帽海贼团战斗员",
        "dream": "世界第一大剑豪"
    }
}, {
    "id": 3,
    "pic": "images/03.jpg",
    "descript": {
        "name": "“小贼猫”娜美",
        "identity": "草帽海贼团航海士",
        "dream": "绘制出自己的世界地图、绘制全世界的航海图"
    }
}, {
    "id": 4,
    "pic": "images/04.jpg",
    "descript": {
        "name": "“GOD”▪乌索普",
        "identity": "草帽海贼团狙击手",
        "dream": "成为勇敢的海上战士"
    }
}, {
    "id": 5,
    "pic": "images/05.jpg",
    "descript": {
        "name": "“黑足”山治",
        "identity": "草帽海贼团厨师",
        "dream": "寻找传说中的奇迹之海-ALL BLUE"
    }
}, {
    "id": 6,
    "pic": "images/06.jpg",
    "descript": {
        "name": "托尼托尼·乔巴",
        "identity": "草帽海贼团船医",
        "dream": "成为万能药"
    }
}, {
    "id": 7,
    "pic": "images/07.jpg",
    "descript": {
        "name": "“改造人”弗兰奇",
        "identity": "草帽海贼团船工",
        "dream": "制造出梦想之船"
    }
}, {
    "id": 8,
    "pic": "images/08.jpg",
    "descript": {
        "name": "“鼻歌”&“灵魂之王”布鲁克",
        "identity": "草帽海贼团音乐家",
        "dream": "环绕世界一周到伟大航道双子岬跟伙伴鲸鱼“拉布”重逢"
    }
}, {
    "id": 9,
    "pic": "images/09.jpg",
    "descript": {
        "name": "“恶魔之子”妮可·罗宾",
        "identity": "草帽海贼团考古学家",
        "dream": "在历史正文碑的指引下,到达伟大航道的尽头“拉夫德鲁”"
    }
}]