按照國際慣例先放效果圖 貼代碼: index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> <link rel="stylesheet" href="index.css ...
按照國際慣例先放效果圖
貼代碼:
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' } ]