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面向对象编程之对象使用分析
Aug 19 Javascript
jquery焦点图片切换(数字标注/手动/自动播放/横向滚动)
Jan 24 Javascript
jQuery操作cookie方法实例教程
Nov 25 Javascript
浅谈JS原生Ajax,GET和POST
Jun 08 Javascript
解析预加载显示图片艺术
Dec 05 Javascript
利用HTML5+Socket.io实现摇一摇控制PC端歌曲切换
Jan 13 Javascript
十大 Node.js 的 Web 框架(快速提升工作效率)
Jun 30 Javascript
React入门教程之Hello World以及环境搭建详解
Jul 11 Javascript
VUE重点问题总结
Mar 19 Javascript
Smartour 让网页导览变得更简单(推荐)
Jul 19 Javascript
JavaScript文档加载模式以及元素获取
Jul 28 Javascript
夯基础之手撕javascript继承详解
Nov 09 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
十大感人催泪爱情动漫 第一名至今不忍在看第二遍
2020/03/04 日漫
PHP 命令行工具 shell_exec, exec, passthru, system详细使用介绍
2011/09/11 PHP
基于curl数据采集之单页面并行采集函数get_htmls的使用
2013/04/28 PHP
PHP中数组的分组排序实例
2014/06/01 PHP
详谈PHP程序Laravel 5框架的优化技巧
2016/07/18 PHP
jQuery实现密保互斥问题解决方案
2013/08/16 Javascript
JS获取农历日期具体实例
2013/11/14 Javascript
jQuery使用height()获取高度需要注意的地方
2014/12/13 Javascript
JavaScript插件化开发教程(六)
2015/02/01 Javascript
jQuery插件Slider Revolution实现响应动画滑动图片切换效果
2015/06/05 Javascript
JavaScript中的Math.LOG2E属性使用详解
2015/06/14 Javascript
动态加载jQuery的方法
2015/06/16 Javascript
Jquery中使用show()与hide()方法动画显示和隐藏图片
2015/10/08 Javascript
基于JavaScript代码实现自动生成表格
2016/06/15 Javascript
[01:08]DOTA2次级职业联赛 - Shield战队宣传片
2014/12/01 DOTA
python标准日志模块logging的使用方法
2013/11/01 Python
Python利用ansible分发处理任务
2015/08/04 Python
Python使用multiprocessing实现一个最简单的分布式作业调度系统
2016/03/14 Python
Python 专题六 局部变量、全局变量global、导入模块变量
2017/03/20 Python
Python文件读写保存操作的示例代码
2018/09/14 Python
Python面向对象之类的内置attr属性示例
2018/12/14 Python
Python3.5多进程原理与用法实例分析
2019/04/05 Python
基于python判断目录或者文件代码实例
2019/11/29 Python
Python json格式化打印实现过程解析
2020/07/21 Python
python如何利用Mitmproxy抓包
2020/10/10 Python
加拿大花店:1800Flowers.ca
2016/11/16 全球购物
80年代复古T恤:TruffleShuffle
2018/07/02 全球购物
酒店副总岗位职责
2013/12/24 职场文书
小学生打架检讨书
2014/01/26 职场文书
开展党的群众路线教育实践活动情况汇报
2014/11/05 职场文书
优秀教研组申报材料
2014/12/26 职场文书
怀孕辞职信怎么写
2015/02/28 职场文书
商业计划书格式、范文
2019/03/21 职场文书
原生JS封装vue Tab切换效果
2021/04/28 Vue.js
pycharm部署django项目到云服务器的详细流程
2021/06/29 Python
Java使用jmeter进行压力测试
2021/07/09 Java/Android