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 相关文章推荐
用js写了一个类似php的print_r输出换行功能
Feb 18 Javascript
兼容主流浏览器的iframe自适应高度js脚本
Jan 10 Javascript
JS如何将数字类型转化为没3个一个逗号的金钱格式
Jan 27 Javascript
JS函数重载的解决方案
May 13 Javascript
jQuery类选择器用法实例
Dec 23 Javascript
jQuery实现仿QQ在线客服效果的滚动层代码
Oct 15 Javascript
Angular发布1.5正式版,专注于向Angular 2的过渡
Feb 18 Javascript
用jQuery实现圆点图片轮播效果
Mar 19 Javascript
微信小程序 商城开发(ecshop )简单实例
Apr 07 Javascript
在JS中如何把毫秒转换成规定的日期时间格式实例
May 11 Javascript
微信小程序 wx.request方法的异步封装实例详解
May 18 Javascript
vue使用Element组件时v-for循环里的表单项验证方法
Jun 28 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
用PHP4访问Oracle815
2006/10/09 PHP
php抓即时股票信息
2006/10/09 PHP
PHP isset()与empty()的使用区别详解
2010/08/29 PHP
WordPres对前端页面调试时的两个PHP函数使用小技巧
2015/12/22 PHP
jQuery 使用手册(六)
2009/09/23 Javascript
jQuery fadeTo方法调整图片的透明度使用介绍
2013/05/06 Javascript
alert中断settimeout计时功能
2013/07/26 Javascript
JavaScript设计模式之单例模式实例
2014/09/24 Javascript
node.js实现BigPipe详解
2014/12/05 Javascript
Javascript基础教程之for循环
2015/01/18 Javascript
jQuery实现在列表的首行添加数据
2015/05/19 Javascript
Jquery全屏相册插件zoomvisualizer具有调节放大与缩小功能
2015/11/02 Javascript
jQuery Validate插件实现表单强大的验证功能
2015/12/18 Javascript
图解js图片轮播效果
2015/12/20 Javascript
基于jquery fly插件实现加入购物车抛物线动画效果
2016/04/05 Javascript
AngularJS在IE下取数据总是缓存问题的解决方法
2016/08/05 Javascript
基于vue.js的分页插件详解
2017/11/27 Javascript
使用vue中的v-for遍历二维数组的方法
2018/03/07 Javascript
JQuery常见节点操作实例分析
2019/05/15 jQuery
layer.open组件获取弹出层页面变量、函数的实例
2019/09/25 Javascript
利用TensorFlow训练简单的二分类神经网络模型的方法
2018/03/05 Python
Python中一些不为人知的基础技巧总结
2018/05/19 Python
Python中浅拷贝copy与深拷贝deepcopy的简单理解
2018/10/26 Python
python线程中的同步问题及解决方法
2019/08/29 Python
利用Python实现朋友圈中的九宫格图片效果
2020/09/03 Python
html5 横向滑动导航栏的方法示例
2020/05/08 HTML / CSS
管道维修工岗位职责
2013/12/27 职场文书
办公室前台岗位职责
2014/01/04 职场文书
2014年计算机专业个人自我评价
2014/01/19 职场文书
六五普法规划实施方案
2014/03/21 职场文书
党的群众路线教育实践活动总结报告
2014/04/28 职场文书
汽车维修专业自荐书
2014/05/26 职场文书
我们的节日元宵活动方案
2014/08/23 职场文书
企业挂职心得体会
2014/09/10 职场文书
妈妈别哭观后感
2015/06/08 职场文书
ssh服务器拒绝了密码 请再试一次已解决(亲测有效)
2022/08/14 Servers