按照国际惯例先放效果图

贴代码:
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'
}
]
来源:https://www.cnblogs.com/chenyingying0/p/12588438.html