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 相关文章推荐
My Desktop :) 桌面式代码
Dec 29 Javascript
页面js遇到乱码问题的解决方法是和无法转码的情况
Apr 30 Javascript
js加减乘除丢失精度问题解决方法
May 16 Javascript
浅谈javascript中this在事件中的应用
Feb 15 Javascript
javaScript实现可缩放的显示区效果代码
Oct 26 Javascript
详解 javascript中offsetleft属性的用法
Nov 11 Javascript
BootStrap.css 在手机端滑动时右侧出现空白的原因及解决办法
Jun 07 Javascript
全面解析jQuery $(document).ready()和JavaScript onload事件
Jun 08 Javascript
利用vue写todolist单页应用
Dec 15 Javascript
vue bootstrap小例子一枚
Jun 09 Javascript
详解微信小程序的 request 封装示例
Aug 21 Javascript
react中useState使用:如何实现在当前表格直接更改数据
Aug 05 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运行速度的一些小技巧分享
2012/07/03 PHP
PHP获取windows登录用户名的方法
2014/06/24 PHP
laravel 实现用户登录注销并限制功能
2019/10/24 PHP
js计数器代码
2006/11/04 Javascript
js下利用控制器载入对应脚本
2010/07/17 Javascript
JQuery自适应IFrame高度(支持嵌套 兼容IE,ff,safafi,chrome)
2011/03/28 Javascript
js带前后翻页的图片切换效果代码分享
2015/09/08 Javascript
js匿名函数作为函数参数详解
2016/06/01 Javascript
使用nodejs中httpProxy代理时候出现404异常的解决方法
2016/08/15 NodeJs
AngularJS实现动态添加Option的方法
2017/05/17 Javascript
node.js 核心http模块,起一个服务器,返回一个页面的实例
2017/09/11 Javascript
利用node实现一个批量重命名文件的函数
2017/12/21 Javascript
vue2.0页面前进刷新回退不刷新的实现方法
2018/07/31 Javascript
快速了解Node中的Stream流是什么
2019/02/13 Javascript
js实现简单的秒表
2020/01/16 Javascript
Jquery ajax书写方法代码实例解析
2020/06/12 jQuery
[39:21]LGD vs OG 2019国际邀请赛淘汰赛 胜者组 BO3 第二场 8.24
2019/09/10 DOTA
Python实现设置windows桌面壁纸代码分享
2015/03/28 Python
python入门教程之识别验证码
2017/03/04 Python
Python中工作日类库Busines Holiday的介绍与使用
2017/07/06 Python
Python网络爬虫中的同步与异步示例详解
2018/02/03 Python
Python第三方库h5py_读取mat文件并显示值的方法
2019/02/08 Python
详解pyinstaller生成exe的闪退问题解决方案
2020/06/19 Python
Pandas的Apply函数具体使用
2020/07/21 Python
收集的22款给力的HTML5和CSS3帮助工具
2012/09/14 HTML / CSS
HTML5不支持frameset的两种解决方法
2016/11/14 HTML / CSS
Brora官网:英国领先的羊绒服装品牌
2019/08/28 全球购物
Ado与Ado.net的相同与不同
2014/12/08 面试题
工作会议方案
2014/05/21 职场文书
信用卡工作证明模板
2014/09/14 职场文书
巴黎圣母院观后感
2015/06/10 职场文书
高中团支书竞选稿
2015/11/21 职场文书
python爬虫之利用selenium模块自动登录CSDN
2021/04/22 Python
go结构体嵌套的切片数组操作
2021/04/28 Golang
Python基础详解之邮件处理
2021/04/28 Python
浅谈JS的二进制家族
2021/05/09 Javascript