zoukankan      html  css  js  c++  java
  • ES6实现图片切换特效(图片墙效果)

    按照国际惯例先放效果图

    贴代码:

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>index</title>
        <link rel="stylesheet" href="index.css">
    </head>
    <body>
        <div id="wrap">
            <!-- <div class="img_container">
                <ul class="img_classify">
                    <li class="img_classify_type_btn img_classify_type_btn_active">类别1</li>
                    <li class="img_classify_type_btn">类别2</li>
                </ul>
                <div class="img_pic_container">
                    <figure>
                        <img src="images/1.jpg" alt="1">
                        <figcaption>title</figcaption>
                    </figure>
                </div>
            </div> -->
        </div>
        <!-- 遮罩层,预览时出现大图 -->
        <!-- <div class="img_overlay">
            <div class="img_overlay_prevbtn"></div>
            <div class="img_overlay_nextbtn"></div>
            <img src="images/1.jpg" alt="1">
        </div> -->
    
        <script src="index.js"></script>
        <script src="data.js"></script>
        <script>
            const img=new $Img({
                data,
                initType:"JavaScript",//默认显示的分类
                outWrap:"#wrap"//所有DOM挂载点
            });
        </script>
    </body>
    </html>

    index.css

    *{
        margin:0;
        padding:0;
    }
    body{ 
        background: #fafafa;
        background: url('images/bg.png')
    }
    li{
        list-style:none;
    }
    a{
        text-decoration: none;
    }
    ::-webkit-scrollbar {
        display: none;
    }
    #wrap{  
        width: 1065px;
        margin: 0 auto;
        padding: 30px;
        background: rgb(255, 255, 255);
        border-radius: 2px;
        margin-top: 100px;
    }
    .img_container{
        font-size: 10px;
    }
    .img_classify_type_btn{
        display: inline-block;
        padding: .2em 1em;
        font-size: 1.6em;
        margin-right: 10px;
        cursor: pointer;
        border: 1px solid #e95a44;
        outline: none;
        color: #e95a44;
        transition: all .4s;
        user-select: none;/*文字不允许用户选中*/
        border-radius: 2px;
    }
    .img_classify_type_btn_active{
        background: #e95a44;
        color: #fff;
    }
    .img_pic_container{
        position: relative;
        margin-top: 30px;
        width: 1005px;
        display: flex;
        flex-wrap: wrap;
        transition: all .6s cubic-bezier(0.77, 0, 0.175, 1);/*动画效果*/
    }
    .img_pic_container figure{
        width: 240px;
        height: 140px;
        position: absolute;
        transition: all .6s cubic-bezier(0.77, 0, 0.175, 1);
        transform: scale(0, 0);
        opacity: 0;
        overflow: hidden;
        border-radius: 2px;
        user-select: none;
    }
    /* 伪元素遮罩层 */
    .img_pic_container figure::before {
        display: block;
        position: absolute;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        z-index: 4;
        background: rgba(58, 12, 5, 0.5);
        content: ' ';
        font-size: 0;
        opacity: 0;
        transition: all .3s;
        cursor: pointer;
    }
    /* 图片 */
    .img_pic_container figure img {
        display: block;
        width: 100%;
        height: 100%;
        transition: all .3s;
    }
    /* 图片标题 */
    .img_pic_container figure figcaption {
        position: absolute;
        top: 50%;
        left: 50%;
        z-index: 7;
        opacity: 0;
        font-size: 1.5em;
        color: rgb(255, 255, 255);
        transform: translate(-50%, -50%);
        transition: all .3s;
        text-align: center;
        cursor: pointer;
    }
    /* 悬停图片的时候标题显示 */
    .img_pic_container figure:hover figcaption{
        opacity: 1;
    }
    .img_pic_container figure:hover img{
        transform: scale(1.1, 1.1);
    }
    /* 悬停图片的时候遮罩显示 */
    .img_pic_container figure:hover::before{
        opacity: 1;
    }
    .img_overlay{
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-color: rgba(0, 0, 0, .8);
        display: flex;
        justify-content: center;
        align-items: center;
        opacity: 0;
        transition: all .3s;
        display: none;
        z-index: 99;
    }
    .img_overlay_prevbtn,
    .img_overlay_nextbtn{
        position: absolute;
        width: 50px;
        height: 50px;
        border-radius: 50%;
        border: 2px solid white;
        text-align: center;
        line-height: 50px;
        color: white;
        font-size: 2rem;
        cursor: pointer;
    }
    .img_overlay_prevbtn{
        left: 20px;
    }
    .img_overlay_nextbtn{
        right: 20px;
    }
    .img_overlay_prevbtn:active,
    .img_overlay_nextbtn:active{
        background: rgb(241, 241, 241, .4);
    }
    .img_overlay_nextbtn::after{
        content: "N";
    }
    .img_overlay_prevbtn::after{
        content: "P";
    }
    .img_overlay img {
      transform: scale(2, 2);
    }

    index.js

    (function(window,document){
    
        let canChange=true;
        let curImgIndex=0;//默认显示的图片索引
    
        //公共方法(便于之后对DOM的操作)
        const methods={
            //同时添加多个子元素,对象简洁表示法
            appendChilds(parent,...child){
                child.forEach(item=>{
                    parent.appendChild(item);
                })
            },
            //选择单个元素
            $(selector,root=document){
                return root.querySelector(selector);
            },
            //选择多个元素
            $$(selector,root=document){
                return root.querySelectorAll(selector);
            }
        };
    
        // 构造函数
        let Img=function(options){
            this._init(options);//初始化,对图片进行分类
            this._createElement();//生成DOM
            this._bind();//绑定事件
            this._show();//显示到页面上
        }
    
        //初始化
        Img.prototype._init=function({data,initType,outWrap}){
            this.types=["全部"];//全部分类
            this.all=[];//所有图片
            this.classified={"全部":[]};//分类映射
            this.curType=initType;//当前显示的图片分类
            this.outWrap=methods.$(outWrap);//所有DOM挂载点
            this.imgContainer=null;//图片部分容器(不包括分类按钮)
            this.wrap=null;//图片区域总容器(包括分类按钮)
            this.typeBtnEls=null;//分类按钮数组
            this.figures=null;//图片数组
    
            this._classify(data);//对图片进行分类
            //console.log(this.classified);//打印分类映射表
    
        }
    
        //对图片进行分类
        Img.prototype._classify=function(data){
            let srcs=[];//存储已经生成过的图片,避免重复生成
    
            data.forEach(({type,title,alt,src},index)=>{
                // arr.includes(a) 判断数组中是否存在某个值
                // 如果分类的数组中,没有当前分类,则添加当前分类
                if(!this.types.includes(type)){            
                    this.types.push(type);
                }
    
                //Object.keys(obj) 返回obj中所有属性名组成的数组
                //如果属性名中不存在该分类,则添加该分类
                if(!Object.keys(this.classified).includes(type)){
                    this.classified[type]=[];
                }
    
                //如果该图片没有生成过
                if(!srcs.includes(src)){
                    srcs.push(src);
    
                    //生成图片
                    let figure=document.createElement("figure");
                    let img=document.createElement("img");
                    let figcaption=document.createElement("figcaption");
    
                    img.src=src;
                    img.setAttribute("alt",alt);
                    figcaption.innerText=title;
    
                    methods.appendChilds(figure,img,figcaption);
    
                    //添加到图片数组中
                    this.all.push(figure);
    
                    //添加到分类映射中
                    this.classified[type].push(this.all.length-1);
    
                }else{
                    //如果该图片已经生成过,就去srcs数组中找到对应图片
                    //srcs.findIndex(s1=>s1===src) 遍历src数组,找到元素的值为src的,返回其下标
                    this.classified[type].push(srcs.findIndex(s1=>s1===src));
    
                }
            })
        }
    
        //获取对应分类下的图片
        Img.prototype._getImgsByType=function(type){
            //如果分类是全部,就返回all数组
            //否则就去图片映射表里,找到该分类对应的图片的索引;
            //通过map遍历this.all数组,找到这些索引对应的图片
            return type==="全部"?[...this.all]:this.classified[type].map(index=>this.all[index]);
        }
    
        //生成DOM
        Img.prototype._createElement=function(){
            let typesBtn=[];
            //根据分类数组,生成所有分类对应的按钮元素
            for(let type of this.types.values()){    
                typesBtn.push(`
                    <li class="img_classify_type_btn${ type===this.curType?' img_classify_type_btn_active':''}">${ type }</li>
                `);
            }
    
            //console.log(typesBtn);
    
            //整体模板
            let templates=`
                <ul class="img_classify">${ typesBtn.join("") }</ul>
                <div class="img_pic_container"></div>
            `;
    
            let wrap=document.createElement("div");
            wrap.className="img_container";
            wrap.innerHTML=templates;
    
            this.imgContainer=methods.$(".img_pic_container",wrap);
    
            //将分类下对应的图片数组使用扩展运算符展开,添加到图片容器中
            methods.appendChilds(this.imgContainer,...this._getImgsByType(this.curType));
    
            //取出可能会用到的元素,挂载到this上
            this.wrap=wrap;
            this.typeBtnEls=[...methods.$$(".img_classify_type_btn",wrap)];
            this.figures=[...methods.$$("figure",wrap)];//使用扩展运算符将取得的DOM元素转数组
    
            //遮罩层
            let overlay=document.createElement("div");
            overlay.className="img_overlay";
            overlay.innerHTML=`
                <div class="img_overlay_prevbtn"></div>
                <div class="img_overlay_nextbtn"></div>
                <img src="" alt="">
            `;
            methods.appendChilds(this.outWrap,overlay);
            this.overlay=overlay;
            this.previewImg=methods.$("img",overlay);//当前要预览的图片
    
            this._calcPosition(this.figures);//修改每张图片的定位
        }
    
        //映射关系
        Img.prototype._diff=function(curImgs,nextImgs){
            let diffArr=[];//保存映射关系
            // 如:当前[1,2,3,5,6],下一批[3,9,11,12,14]
            // 则映射为[[2,0],..] 
            // 两组图片中国相同的图片为3,3在数组1的下标为2,3在数组2的下标为0,保存这种映射关系
            
            curImgs.forEach((src1,index1)=>{
                //遍历当前src数组,对每一个src,去下一批的src数组中找是否有相同的,有则返回该src在下一批数组中的下标
                let index2=nextImgs.findIndex(src2=>src1===src2);
    
                //
                if(index2!=-1){
                    diffArr.push([index1,index2]);
                }
            })
    
            return diffArr;
    
        }
    
        //绑定事件
        Img.prototype._bind=function(){
            //解构赋值,获取到e.target
            methods.$(".img_classify",this.wrap).addEventListener("click",({target})=>{
                if(target.nodeName!=="LI") return;//如果点的不是li,则返回
                //console.log(target.innerText);
                
                if(!canChange) return;
                canChange=false;
    
                const type=target.innerText;
                const imgs=this._getImgsByType(type);//下一轮要显示的所有图片
    
                //目前出现的图片的所有src
                let curImgs=this.figures.map(figure=>methods.$("img",figure).src);
                //点击后下一批要出现的图片的src
                let nextImgs=imgs.map(figure=>methods.$("img",figure).src);
    
                const diffArr=this._diff(curImgs,nextImgs);//得到两组图片共有的图片映射关系
                //遍历该映射
                diffArr.forEach(([,index2])=>{//index2是两组中相同的图片在下一组中的索引
                    //every() 方法使用指定函数检测数组中的所有元素:
                    //如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。
                    //如果所有元素都满足条件,则返回 true。
                    this.figures.every((figure,index)=>{
                        let src=methods.$("img",figure).src;
                        if(src===nextImgs[index2]){
                            //splice() 方法向/从数组中添加/删除项目
                            this.figures.splice(index,1);//找到相同的图片,从上一轮的图片数组中剔除
                            return false;
                        }
                        return true;
                    })
                })
    
                this._calcPosition(imgs);
    
                let needAppendImgs=[];//切换下一轮时需要添加的元素(相同的元素不需要重复添加)
                if(diffArr.length){
                    //如果两轮存在相同图片,则相同的图片 不需要重复加载
                    let nextIndex=diffArr.map(([,index2])=>index2);//相同的图片的下标
                    imgs.forEach((figure,index)=>{
                        if(!needAppendImgs.includes(index)){
                            needAppendImgs.push(figure);//如果该图片不存在,则添加
                        }
                    })
                }else{
                    //如果不存在相同图片,则所有图片需要加载
                    needAppendImgs=imgs;
                }
    
                //隐藏当前所有图片
                this.figures.forEach(figure=>{
                    figure.style.transform="scale(0,0) translate(0 100%)";
                    figure.style.opacity="0";
                })
    
                //添加需要新增的图片
                methods.appendChilds(this.imgContainer,...needAppendImgs);
    
                //显示新一轮的图片            
                setTimeout(()=>{
                    imgs.forEach(el=>{                    
                        el.style.transform="scale(1,1) translate(0,0)";
                        el.style.opacity="1";    
                        //console.log(el);                
                    })
                });
    
                //销毁上一轮出现的所有图片
                setTimeout(()=>{
                    this.figures.forEach(figure=>{
                        this.imgContainer.removeChild(figure);            
                    })
    
                    this.figures=imgs;
    
                    canChange=true;
                },600);//在css中动画设置的结束时长就是0.6秒
    
                //切换按钮样式
                this.typeBtnEls.forEach(btn=>(btn.className="img_classify_type_btn"));
                target.className="img_classify_type_btn img_classify_type_btn_active";
            })
    
            //给每张图片绑定点击事件
            this.imgContainer.addEventListener("click",({target})=>{
                if(target.nodeName!=="FIGURE" && target.nodeName!=="FIGCAPTION") return;
                if(target.nodeName==="FIGCAPTION"){
                    target=target.parentNode;
                }
    
                //显示预览的图片和遮罩层
                const src=methods.$("img",target).src;
                curImgIndex=this.figures.findIndex(figure=>src===methods.$("img",figure).src);
                this.previewImg.src=src;
                this.overlay.style.display="flex";
                
                setTimeout(()=>{
                    this.overlay.style.opacity="1";
                })
            })
    
            //点击遮罩层,淡出
            this.overlay.addEventListener("click",()=>{
                this.overlay.style.opacity="0";
                setTimeout(()=>{
                    this.overlay.style.display="none";
                },300);//300毫秒是css中transition设置的时间
            })
    
            //上一张下一张按钮绑定
            methods.$(".img_overlay_prevbtn",this.overlay).addEventListener("click",e=>{
                //阻止事件冒泡,导致触发点击遮罩隐藏遮罩的情况
                e.stopPropagation();
                curImgIndex=curImgIndex===0?this.figures.length-1:curImgIndex-1;
                this.previewImg.src=methods.$("img",this.figures[curImgIndex]).src;
            })
            methods.$(".img_overlay_nextbtn",this.overlay).addEventListener("click",e=>{
                e.stopPropagation();
                curImgIndex=curImgIndex===this.figures.length-1?0:curImgIndex+1;
                this.previewImg.src=methods.$("img",this.figures[curImgIndex]).src;
            })
        }
    
        //显示到页面上
        Img.prototype._show=function(){
            methods.appendChilds(this.outWrap,this.wrap);//把图片总容器挂载到指定的外容器上
    
            //演示器实现进场动画
            setTimeout(()=>{
                //让所有图片显示
                this.figures.forEach(figure=>{
                    figure.style.transform="scale(1,1) translate(0,0)";
                    figure.style.opacity="1";
                })
            },0)
    
        }
    
        //计算每张图片的top和left
        Img.prototype._calcPosition=function(figures){
            figures.forEach((figure,index)=>{
                //140是每张图片的高度,15是每张图片垂直方向的间隙
                //240是每张图片的宽度,15是每张图片水平方向的间隙
                figure.style.top=parseInt(index/4)*140+parseInt(index/4)*15+"px";
                figure.style.left=parseInt(index%4)*(240+15)+"px";
                figure.style.transform="scale(0,0) translate(0,-100%)";//让特效更丰富
            })
    
            //设置图片容器高度
            var num=figures.length;//图片数量
            if(num<=4){
                this.imgContainer.style.height="140px";
            }else{
                this.imgContainer.style.height=Math.ceil(num/4)*140+(Math.ceil(num/4)-1)*15+"px";
            }
            
        }
    
        window.$Img=Img;//暴露到全局
    
    })(window,document);

    data.js(数据)

    const data = [
    
      {
        type: 'JavaScript',
        title: 'ES6快速入门',
        alt: 'ES6快速入门',
        src: './images/1.jpg'
      },
    
      {
        type: 'JavaScript',
        title: 'Javascript实现二叉树算法',
        alt: 'Javascript实现二叉树算法',
        src: './images/2.jpg'
      },
    
      {
        type: 'JavaScript',
        title: 'Canvas绘制时钟',
        alt: 'Canvas绘制时钟',
        src: './images/3.jpg'
      },
    
      {
        type: 'JavaScript',
        title: '基于websocket的火拼俄罗斯',
        alt: '基于websocket的火拼俄罗斯',
        src: './images/15.jpg'
      },
    
      {
        type: '前端框架',
        title: 'React知识点综合运用实例',
        alt: 'React知识点综合运用实例',
        src: './images/4.jpg'
      },
    
      {
        type: '前端框架',
        title: 'React组件',
        alt: 'React组件',
        src: './images/5.jpg'
      },
    
      {
        type: '前端框架',
        title: 'Vue+Webpack打造todo应用',
        alt: 'Vue+Webpack打造todo应用',
        src: './images/6.jpg'
      },
    
      {
        type: '前端框架',
        title: 'Vue.js入门基础',
        alt: 'Vue.js入门基础',
        src: './images/7.jpg'
      },
    
      {
        type: '前端框架',
        title: '使用Vue2.0实现购物车和地址选配功能',
        alt: '使用Vue2.0实现购物车和地址选配功能',
        src: './images/8.jpg'
      },
    
      {
        type: 'React',
        title: 'React知识点综合运用实例',
        alt: 'React知识点综合运用实例',
        src: './images/4.jpg'
      },
    
      {
        type: 'React',
        title: 'React组件',
        alt: 'React组件',
        src: './images/5.jpg'
      },
    
      {
        type: 'Vue.js',
        title: 'Vue+Webpack打造todo应用',
        alt: 'Vue+Webpack打造todo应用',
        src: './images/6.jpg'
      },
    
      {
        type: 'Vue.js',
        title: 'Vue.js入门基础',
        alt: 'Vue.js入门基础',
        src: './images/7.jpg'
      },
    
      {
        type: 'Vue.js',
        title: '使用Vue2.0实现购物车和地址选配功能',
        alt: '使用Vue2.0实现购物车和地址选配功能',
        src: './images/8.jpg'
      }
    
    ]
  • 相关阅读:
    RGB空间与HSV空间的相互转换(C++实现,修正网上大多数的代码错误)
    SLIC superpixel实现分析
    开发自己PHP MVC框架(一)
    C++ 直方图匹配算法代码
    准确率与召回率
    Github干货系列:C++资源集合-
    ezw证件照芯片压缩算法
    格拉姆-施密特正交化
    [轉]sendpage漏洞分析 CVE-2009-2692
    ptrace
  • 原文地址:https://www.cnblogs.com/chenyingying0/p/12588438.html
Copyright © 2011-2022 走看看