webProject demo-2 Images/3DchangeImg

先上效果

https://rencoo.github.io/webproject/images/3DchangImg/index.html

完成目标

  1. 轮播图片的组件化
  2. API 和 控制流 分开
  3. 设计了较高级的API用于收集回调函数
  4. CSS的 transform 和 transform-origin 元素的转换(缩放、移动等)功能
  5. CSS的 transition 平滑过渡样式功能的使用
  6. 通过两个 <span> 来达到 UI需要的显示效果与鼠标点击事件兼顾

CODE部分

<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title>3D图片轮播图</title>
        <link rel="stylesheet" href="change.css">
    </head>
    <body>
        <div id="my-slider">

            <!-- 轮播图结构 -->
            <div class="slider-list">
                <ul>
                    <li class="p1"><img src="img/1.png" alt="" /></li>
                    <li class="p2"><img src="img/2.png" alt="" /></li>
                    <li class="p3-selected"><img src="img/3.jpg" alt="" /></li>
                    <li class="p4"><img src="img/4.png" alt="" /></li>
                    <li class="p5"><img src="img/5.jpg" alt="" /></li>
                    <li class="p6"><img src="img/6.jpg" alt="" /></li>
                    <li class="p7"><img src="img/7.jpg" alt="" /></li>
                </ul>
            </div>

            <!-- 控制流 UI -->
            <div class="slider-control">
            <!-- 使用两个<span>的说明:由于里面按钮ui效果厚度需要很小,在窗口里不好点击到(触发事件);因此在外面套了一个<span>,用于绑定鼠标点击事件-->
                <span><span></span></span>
                <span><span></span></span>
                <span class="btn-selected"><span></span></span>
                <span><span></span></span>
                <span><span></span></span>
                <span><span></span></span>
                <span><span></span></span>
            </div>

            <span class="prev btn"><</span>
            <span class="next btn">></span>
        </div>
        
        <!-- 在组件外,也能灵活调用 API -->
        <div id="sliderState"><p>当前第 3 页</p></div>

    </body>
    <script src="change.js"></script>
</html>
* {
    margin: 0;
    padding: 0;
}

#my-slider {
    position: relative;
    width: 100%;
    height: 340px;
    margin-top: 100px;
}

#my-slider>.slider-list{
    position: absolute;
    left: 50%;
    width: 1200px;
    height: 300px;
    margin-left: -600px;
    overflow: hidden;
}

li {
    position: absolute;
    top: 0;
    left: 0;
    width: 750px;
    height: 300px;
    list-style: none;
    opacity: 0;
    transition: all 0.3s ease-in-out; 
}

li>img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
}

.p1 {
    transform:translate3d(-224px,0,0) scale(0.81);
}

.p2 {
    transform:translate3d(0px,0,0) scale(0.81);
    transform-origin:0 50%;
    z-index: 2;
    opacity: 0.8;
}

.p3-selected {
    transform:translate3d(224px,0,0) scale(1);
    z-index: 3;
    opacity: 1;
}

.p4 {
    transform:translate3d(448px,0,0) scale(0.81);
    transform-origin:100% 50%;
    z-index: 2;
    opacity: 0.8;
}

.p5 {
    transform:translate3d(672px,0,0) scale(0.81);
}

.p6 {
    transform:translate3d(896px,0,0) scale(0.81);
}

.p7 {
    transform:translate3d(1120px,0,0) scale(0.81);
}   

.btn {
    position: absolute;
    top: 50%; 
    width: 60px;
    height: 100px;
    margin-top: -50px;
    background: black;
    opacity: 0.2;
    line-height: 100px;
    font-size: 30px;
    color: white;
    text-decoration: none;
    text-align: center;
    cursor: pointer;
}

.next{
    right: 0;
}

.slider-control {
    position: absolute;
    bottom: 0;
    left: 50%;
    width: 1200px;
    height: 30px;
    margin-left: -600px;
    text-align: center;
    padding-top: 10px;
}

.slider-control>span {
    display: inline-block;
    width: 35px;
    height: 100%;
    cursor: pointer;
}

.slider-control>span>span {
    position: relative;
    top: 30%;
    display: block;
    width: 100%;
    height: 1px;
    background: red;
}

.slider-control>.btn-selected>span {
    background: blue;
}

#sliderState {
    position: absolute;
    top: 440px;
    width: 100%;
    text-align: center;
}
class Slider {
    constructor(id, cycle=3000){
        this.container = document.getElementById(id);
        this.items= this.container.querySelectorAll('.slider-list li');

        this.cycle = cycle;
        // 回调函数队列;
        this.slideHandlers = []; 

        // UI 控制流部分

        // 鼠标移入组件时清除定时器
        this.container.addEventListener('mouseover', ()=>{
            this.stop();
        })
        
        // 鼠标移出组件时开始定时器
        this.container.addEventListener('mouseout', ()=>{
            this.start();
        })

        // 鼠标点击图片,触发上一张和下一张
        this.container.addEventListener('click', evt=>{
            console.log('picclicked');
            console.log(evt.target.parentElement);
            if (evt.target.parentElement.className != 'p2' && evt.target.parentElement.className != 'p4' ) return;
            else if (evt.target.parentElement.className == 'p2') {
                console.log('前一张');
                this.prevImg();
            } 
            else {
                console.log('下一张');
                this.nextImg();
            }
        })
        
        // 鼠标点击上一张 和 下一张 按钮进行触发 上一张和下一张
        let nextBtn = this.container.querySelector('.next');
        if(nextBtn) {
            nextBtn.addEventListener('click', () => this.nextImg());
        }
        let prevBtn = this.container.querySelector('.prev');
        if(prevBtn) {
            prevBtn.addEventListener('click', () => this.prevImg());
        }

        // 鼠标点击底下slider控制按钮进行图片切换
        let controller = this.container.querySelector('.slider-control');
        if (controller) {
            // 加了这个判断之后,整体删除组件的控制部分,程序也能正常运行
            let btns = this.container.querySelectorAll('.slider-control>span');
            controller.addEventListener('click', evt=>{
                var idx = Array.from(btns).indexOf(evt.target);
                
                if(idx >= 0) this.slideTo(idx);
            });

            // 往回调函数队列中增加 "改变底下当前选中按钮的背景色" 的回调函数
            this.addSlideListener(function(idx) {
                let selected = controller.querySelector('.btn-selected');
                if(selected) selected.className = '';
                btns[idx].className = 'btn-selected'; // 次数 褚className 不用加点
            });
        }
    }

    // API 部分

    // 获取照片CSS布局定位的 class
    // ["p1", "p2", "p3-selected", "p4", "p5", "p6", "p7"];
    getLiClasses(){
        let liClasses = [];
        Array.from(this.items).forEach(li=>{
            liClasses.push(li.className);
        })
        return liClasses;
    }

    // 获取当前展示的图片
    getSelectedItem() {
        let selected = this.container.querySelector('.p3-selected');
        return selected;
    }

    // 获取当前展示的图片的编号
    getSelectedItemIndex() {
        return Array.from(this.items).indexOf(this.getSelectedItem());
    }

    // 跳到要展示的目标图片(核心API)
    slideTo(idx) {
        let currentIdx = this.getSelectedItemIndex(); 
        let liClasses = this.getLiClasses();
        
        let item = this.items[idx];
        if(item){
            if(idx == currentIdx) {
                return;
            }

            else if(idx < currentIdx){
                Array.from(this.items).forEach((item, i) => {
                    item.className = '';
                    item.className = liClasses[(i + (currentIdx - idx)) % liClasses.length];
                })
            }

            else if(idx > currentIdx){
                Array.from(this.items).forEach((item, i)=>{
                    item.className = ''; 
                    item.className = liClasses[(i + liClasses.length -(idx - currentIdx)) % liClasses.length];
                })
            }
        }
        
        // 执行回调函数队列里的函数:
        // 1. 改变底下按钮中当前选中按钮的背景色
        // 2. 显示当前第几页
        this.slideHandlers.forEach(handler=>{
            handler(idx);
        });
    }
    
    // 下一张图片
    nextImg() {
        let currentIdx = this.getSelectedItemIndex();
        let nextIdx = (currentIdx + 1) % this.items.length;
        this.slideTo(nextIdx);
    }

    // 上一张图片
    prevImg() {
        let currentIdx = this.getSelectedItemIndex();
        let prevIdx = (currentIdx + this.items.length - 1) % this.items.length;
        this.slideTo(prevIdx);
    }

    // 启动计时器
    start() {
        this.stop();
        this._sliderTimer = setInterval(() => {
            this.nextImg();
        }, this.cycle);
    }

    // 清除计时器
    stop() {
        clearInterval(this._sliderTimer);
    }

    // 高级API; 用于收集回调函数
    addSlideListener(handler) {
        this.slideHandlers.push(handler);
    }
}

// 调用
const slider = new Slider('my-slider');
// 启动定时器
slider.start();

// 在组件外部往 回调函数队列 添加函数
slider.addSlideListener(function(idx){
    sliderState.textContent = `当前第 ${idx + 1} 页`;
});

猜你喜欢

转载自www.cnblogs.com/rencoo/p/9381499.html