js实现3D照片墙效果


Posted in Javascript onOctober 28, 2019

聊一下心得:CSS写得好,真的可以省很多js代码哈,写起来也简单很多,所以要好好掌握js哈,所以这里也提供了css代码,如果您觉得您的css写得不错,可以直接看js代码哦

效果:

1、点击Start View进入照片墙

2、只有一张图片是在中间显示,其他图片在中间的图片两侧随机排序,并且随机旋转一定的角度,层级也是随机的哦

3、点击上面的导航条,可以让对应的图片在中间显示

4、点击中间的图片该照片翻转,显示背面(照片的描述信息)

实现过程:

1、用数据生成结构(模拟的数据,此处不再提供)

2、对所有图片进行排序

3、计算两侧图片的随机范围

4、控制图片翻转

5、控制导航按钮切换图片

6、遮罩层动画实现

HTML代码:

<body>
 <div class="photo_wall">
 <div class="photo">
 <!-- 每张图片的最外层,用来控制图片的旋转和位移 -->
 <div class="photo_i front" id="photo_{{id}}">
 <!-- 内层用来控制图片的3D翻转 -->
 <div class="photo_3d">
 <!-- 每个照片的正面 -->
 <div class="photo_side photo_front">
 <p><img {{src}}="{{img}}"></p>
 <h3>{{caption}}</h3>
 </div>
 <!-- 每个照片的反面 -->
 <div class="photo_side photo_back">
 <p class="desc">{{desc}}</p>
 </div>
 </div>
 </div>
 {{split}}
 <div class="nav"><span class="nav_i" id="nav_{{id}}">?</span></div>
 </div>
 <div class="shade">
 <div class="start">Start View</div>
 </div>
 </div>
</body>

CSS代码:

/*最外层样式*/
.photo_wall{
 width: 100%;
 height: 600px;
 position: relative;
 background: url(../imgs/bg.jpg) no-repeat center center;
 background-size: cover;
 overflow: hidden;
}
 
/*照片区域的样式*/
.photo {
 position: absolute;
 left: 0;
 top: 0;
 width: 100%;
 height: 100%;
 z-index: 1;
 opacity: 0;
 transition: 1s;
}
 
/*每个照片的样式*/
.photo .photo_i,.photo .photo_3d,.photo .photo_side {
 width: 336px;
 height: 392px;
 position: absolute;
 left: 0;
 top: 0;
}
 
.photo .photo_i {
 transition: 800ms;
 perspective: 750px;
 left: 50%;
 top: 50%;
 transform: translate(-50%,-50%) scale(.5) rotate(0);
}
 
.photo .photo_3d {
 transition: 500ms;
 transform-style: preserve-3d;
 transform-origin: 0 50%;
}
/*正面和反面的公共样式*/
.photo .photo_side {
 border-radius: 6px;
 background: #fff;
 padding: 26px 24px;
 box-sizing: border-box;
 backface-visibility: hidden;
}
 
/*照片的正面样式*/
.photo .photo_front {
 transform: rotateY(0);
}
.photo .photo_front p {
 width: 286px;
 height: 286px;
 border: 2px solid #d8536d;
 overflow: hidden;
 display: flex;
 /*align-items: center;*/
}
.photo .photo_front p img{
 width: 100%;
 align-self: center;
}
.photo .photo_front h3{
 width: 166px;
 height: 44px;
 background: #d8536d;
 border-radius: 0 0 6px 6px;
 margin: 0 auto;
 text-align: center;
 font: 16px/44px Arial;
 color: #fff;
}
/*照片的反面样式*/
.photo .photo_back {
 transform: rotateY(-180deg);
}
 
.photo .photo_back .desc {
 font-size: 14px;
 line-height: 20px;
 color: #d8536d;
}
.photo .photo_back a {
 color: #d8356d;
}
/*照片的居中样式*/
.photo .center {
 z-index: 9999;
 left: 50%;
 top: 50%;
 transform: translate(-50%,-50%) scale(1) rotate(0);
}
 
/*照片正面的class*/
.photo .front .photo_3d {
 transform: translateX(0) rotateY(0);
}
/*照片反面的calss*/
.photo .back .photo_3d {
 transform: translateX(100%) rotateY(-180deg);
}
 
/*导航栏的样式*/
.nav {
 position: absolute;
 left: 0;
 top: 0;
 z-index: 888;
 width: 100%;
 height: 200px;
 padding-top: 10px;
 box-sizing: border-box;
 text-align: center;
 background: -webkit-linear-gradient(top,rgba(0,0,0,.5),transparent);
}
 
@font-face {
 font-family: "icont";
 src: url(../font/iconfont.woff) format("woff");
}
 
.nav .nav_i {
 display: inline-block;
 width: 30px;
 height: 30px;
 border-radius: 50%;
 background: rgba(255,255,255,.5);
 font-family: "icont";
 text-align: center;
 line-height: 30px;
 color: rgba(255,255,255,0);
 cursor: pointer;
 transform: scale(.5);
 transition: 500ms;
}
 
.nav .active {
 color: rgba(255,255,255,1);
 transform: scale(.9) rotateY(0);
}
 
.nav .back {
 transform: scale(.8) rotateY(-180deg);
}
 
/*遮罩层*/
.photo_wall .shade {
 position: absolute;
 left: 0;
 top: 0;
 z-index: 2;
 width: 100%;
 height: 100%;
 background: rgba(255,255,255,.7);
 display: flex;
 justify-content: center;
 align-items: center;
}
.photo_wall .hide {
 transition: 1s;
 opacity: 0;
 transform: scale(0) rotateY(360deg);
}
.photo_wall .shade .start {
 width: 200px;
 height: 60px;
 border: 2px solid #d8536d;
 border-radius: 10px;
 background: rgba(248,229,227,.5);
 text-align: center;
 font: 22px/60px Arial;
 cursor: pointer;
}

js代码:用到了我昨天在博客上写的工具函数:

// 用来获取元素
// 用来判断某个元素是否有某个class
// 如果没有添加
// 如果有就删除
 
//获取元素id class tag all
function M(sele) {
 var first = sele.substr(0,1),
 isArr = sele.split(' ');//id class tag
 if(first==="#"&&isArr.length==1){//id
 return document.getElementById(sele.substr(1));
 }else{
 var arr = Array.from(document.querySelectorAll(sele));
 return arr.length == 1?arr[0] :arr;
 }
}
 
//判断某个元素是否包含某个class
function hasClass(obj,cls){
 var re = new RegExp(`\\b${cls}\\b`);
 if(re.test(obj.className)){
 return true;
 }else{
 return false;
 }
}
 
//给某个元素添加class
function addClass(obj,cls){
 if(!hasClass(obj,cls)){
 obj.className += ` ${cls}`;//不要忘了前面的空格哈
 }
 obj.className = obj.className.trim();//去掉前后空格
}
 
//给某个元素删除class
function rmClass(obj,cls){
 var re = new RegExp(`\\b${cls}\\b`);
 if(hasClass(obj,cls)){
 obj.className = obj.className.replace(re,'')
  .replace(/\s{2}/,' ').trim();//去掉前后空格
 }
}

提供主要的实现步骤的js代码:

(function () {
 
//---------------------------------------------------------
// 初始化数据
 var data = dataList,len = data.length;
 
 createPhotos(data);
 
 var n = 0;
 
//---------------------------------------------------------
// 基本逻辑
 M('.shade .start').addEventListener('click',function() {
 addClass(M('.shade'),'hide');
 M('.photo').style.opacity = 1;
 addClass(M(`#photo_0`),'center');
 setTimeout(function(){
  sortImgs(n);
 }, 200);
 });
 
 M('.nav_i').forEach((item,i)=>{
 item.onclick = function(){
  turnImg(M(`#photo_${i}`));
 };
 });
 
//---------------------------------------------------------
// 需求函数化
 
// 需求1:利用数据生成所有html结构
 function createPhotos(data) {
 var photo_html = M('.photo').innerHTML.split('{{split}}')[0].trim(),
  nav_html = M('.nav').innerHTML.trim();
 
 var photos = [],nav = [];
 
 data.forEach((item,i)=>{
  var photoTemp = photo_html.replace(/{{id}}/,i)
     .replace(/{{src}}/,'src')
     .replace(/{{img}}/,item.img)
     .replace(/{{caption}}/,item.caption)
     .replace(/{{desc}}/,item.desc),
  navTemp = nav_html.replace(/{{id}}/,i);
 
  photos.push(photoTemp);
  nav.push(navTemp);
 });
 photos.push(`<div class="nav">${nav.join('')}</div>`);
 M('.photo').innerHTML = photos.join('');
 }
 
 // 需求2:给所有的图片排序
 function sortImgs(n) {
 var photos = M('.photo_i');
 
 initPhotos(photos);
 
 var center = photos.splice(n,1)[0];
 addClass(center,'center');
 addClass(M(`#nav_${n}`),'active');
 
 // center.addEventListener('click', function(e){
 // turnImg(this);
 // });
 
 center.onclick = function () {
  turnImg(this);
 };
 
 // 对剩余的图片进行随机排序
 photos.sort(()=>{
  return 0.5 - Math.random();
 })
 
 
 var rP = scope(); //返回左右两侧范围 从 x - y
 
 // 分成左侧和右侧两部分
 var left = photos.splice(0,Math.ceil((len-1)/2)),
  right = photos;
 
 left.forEach((item,i)=>{
  item.style.zIndex = rn([0,len]);
  item.style.left = rn(rP.L.x) + 'px';
  item.style.top = rn(rP.L.y) + 'px';
  item.style.transform = `translate(0,0) scale(.9) rotate(${rn([-2160,2160])}deg)`;
 });
 right.forEach((item,i)=>{
  item.style.zIndex = rn([0,len]);
  item.style.left = rn(rP.R.x) + 'px';
  item.style.top = rn(rP.R.y) + 'px';
  item.style.transform = `translate(0,0) scale(.9) rotate(${rn([-2160,2160])}deg)`;
 });
 }
 
 // 需求3 编写某个区间的随机整数
 function rn(arr) {
 var max = Math.max.apply(null,arr),
  min = Math.min.apply(null,arr);
 var p = Math.round(Math.random() * (max - min) + min);
 //?;
 return p;
 }
 
 // 需求4 计算随机的范围
 function scope() {
 var outer = M('.photo_wall');
 var pic = M(`#photo_${rn([0,len-1])}`);
 var W = outer.clientWidth,
  H = outer.clientHeight,
  w = pic.offsetWidth,
  h = pic.offsetHeight;
  console.log(W,w);
 var data = {
  L:{
  x:[-w/3,W/2 - w/2 - w],
  y:[-h/3,H - h*2/3]
  },
  R:{
  x:[W/2 + w/2,W - w*2/3],
  y:[-h/3,H - h*2/3]
 
  }
 }
 return data;
 }
 
 // 需求5:控制图片翻转
 function turnImg(ele) {
 var cur = ele.id.split('_')[1];
 var nav = M(`#nav_${cur}`);
 
 if(!hasClass(ele,'center')){ //如果点的不是当前对应的按钮就重新排序
  return sortImgs(cur)
 }
 
 if(hasClass(ele,'front')){
  //翻转到背面
  console.log('现在是正面准备移除front');
  addClass(ele,'back');
  console.log(ele.className);
  rmClass(ele,'front');
  console.log(ele.className);
  addClass(nav,'back');
 }else{
  //翻转到正面
  console.log('现在是反面准备移除back');
  addClass(ele,'front');
  console.log(ele.className);
  rmClass(ele,'back');
  console.log(ele.className);
  rmClass(nav,'back')
 }
 }
 
 // 需求6 初始化所有样式
 function initPhotos(objs) {
 objs.forEach((item,i)=>{
  if(hasClass(item,'center')){
  var nav = M(`#nav_${i}`);
  rmClass(item,'center');
  rmClass(item,'back');
  addClass(item,'front');
  rmClass(nav,'active');
  rmClass(nav,'back');
  item.onclick = null;
  }
  item.style.left = '';
  item.style.top = '';
  item.style.zIndex = '';
  item.style.transform = `translate(-50%,-50%) scale(1.1) rotate(0deg)`;
 });
 }
 
})()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript 对Cookie 操作的封装小结
Dec 31 Javascript
用jquery和json从后台获得数据集的代码
Nov 07 Javascript
通过js动态操作table(新增,删除相关列信息)
May 23 Javascript
HTML页面滚动时获取离页面顶部的距离2种实现方法
Sep 05 Javascript
在JavaScript中重写jQuery对象的方法实例教程
Aug 25 Javascript
微信分享的标题、缩略图、连接及描述设置方法
Oct 14 Javascript
mescroll.js上拉加载下拉刷新组件使用详解
Nov 13 Javascript
js+html5 canvas实现ps钢笔抠图
Apr 28 Javascript
微信小程序点餐系统开发常见问题汇总
Aug 06 Javascript
vue实现广告栏上下滚动效果
Nov 26 Vue.js
基于element-ui封装表单金额输入框的方法示例
Jan 06 Javascript
JS ES6异步解决方案
Apr 29 Javascript
vue+webpack 更换主题N种方案优劣分析
Oct 28 #Javascript
使用Vue调取接口,并渲染数据的示例代码
Oct 28 #Javascript
JavaScript 反射和属性赋值实例解析
Oct 28 #Javascript
vue 解决数组赋值无法渲染在页面的问题
Oct 28 #Javascript
在vue中把含有html标签转为html渲染页面的实例
Oct 28 #Javascript
详解关闭令人抓狂的ESlint 语法检测配置方法
Oct 28 #Javascript
Vue实现将数据库中带html标签的内容输出(原始HTML(Raw HTML))
Oct 28 #Javascript
You might like
PHP中使用FFMPEG获取视频缩略图和视频总时长实例
2014/05/04 PHP
PHP实现数组array转换成xml的方法
2016/07/19 PHP
微信支付PHP SDK ―― 公众号支付代码详解
2016/09/13 PHP
Laravel学习教程之本地化模块
2017/08/18 PHP
MooTools 1.2介绍
2009/09/14 Javascript
JavaScript 未结束的字符串常量常见解决方法
2010/01/24 Javascript
16个最流行的JavaScript框架[推荐]
2011/05/29 Javascript
测试IE浏览器对JavaScript的AngularJS的兼容性
2015/06/19 Javascript
jQuery+CSS实现滑动的标签分栏切换效果
2015/12/17 Javascript
jquery捕捉回车键及获取checkbox值与异步请求的方法
2015/12/24 Javascript
jquery组件WebUploader文件上传用法详解
2020/10/23 Javascript
浅析javascript中的Event事件
2016/12/09 Javascript
vue脚手架vue-cli的学习使用教程
2017/06/06 Javascript
详解webpack3如何正确引用并使用jQuery库
2017/08/26 jQuery
Vue使用watch监听一个对象中的属性的实现方法
2019/05/10 Javascript
Python构造函数及解构函数介绍
2015/02/26 Python
Python selenium 父子、兄弟、相邻节点定位方式详解
2016/09/15 Python
python DataFrame 修改列的顺序实例
2018/04/10 Python
python 从文件夹抽取图片另存的方法
2018/12/04 Python
Win10+GPU版Pytorch1.1安装的安装步骤
2019/09/27 Python
利用Python发送邮件或发带附件的邮件
2020/11/12 Python
100%植物性、有机、即食餐:Sakara Life
2018/10/25 全球购物
新加坡交友网站:be2新加坡
2019/04/10 全球购物
法国在线药房:Shop Pharmacie
2019/11/26 全球购物
值传递还是引用传递
2015/02/08 面试题
涉外文秘个人求职的自我评价
2013/10/07 职场文书
企业办公室主任岗位职责
2014/02/19 职场文书
计算机专业职业规划
2014/02/28 职场文书
旅游与酒店管理专业求职信
2014/07/21 职场文书
保险公司客户经理岗位职责
2015/04/09 职场文书
无罪辩护词范文
2015/05/21 职场文书
超强台风观后感
2015/06/09 职场文书
教师见习总结范文
2015/06/23 职场文书
导游词之宿迁乾隆行宫
2019/10/15 职场文书
Nginx进程调度问题详解
2021/09/25 Servers
MySQL数据库如何查看表占用空间大小
2022/06/10 MySQL