<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>3D照片墙</title> <style> html, body { margin: 0; padding: 0; background-color: #29454d; /*禁止文字被选中*/ -moz-user-select: none; /*火狐*/ -webkit-user-select: none; /*webkit浏览器*/ -ms-user-select: none; /*IE10*/ -khtml-user-select: none; /*早期浏览器*/ user-select: none; } .box { position: relative; height: 500px; width: 500px; margin: 100px auto; transform-style: preserve-3d; perspective: 2000px; } .di { position: absolute; left: 50%; top: 50%; height: 200px; width: 200px; transform: translate(-50%, -50%) rotatex(90deg); transform-style: preserve-3d; } .z { position: relative; height: 200px; width: 200px; border-radius: 50%; transform-style: preserve-3d; transform: rotatez(1deg); } p { margin: 0; position: absolute; top: 0; /*为了保证圆心在父盒子中心,父盒子旋转时圆心稳定,所以设置left*/ left: 25px; height: 200px; width: 150px; border: 2px solid #fd7068; box-sizing: border-box; background-color: #ffffff; background-size: cover; background-position: center; background-repeat: no-repeat; /*opacity: .7;*/ font-size: 10px; line-height: 200px; text-align: center; /*box-shadow: 0 0 100px #16ff8b;*/ /*倒影,不兼容*/ -webkit-box-reflect: below 10px -webkit-linear-gradient(top, rgba(250, 250, 250, 0), rgba(250, 250, 250, .0) 30%, rgba(250, 250, 250, 0.5)); box-reflect: below 10px -webkit-linear-gradient(top, rgba(250, 250, 250, 0), rgba(250, 250, 250, .0) 30%, rgba(250, 250, 250, 0.5)); /*backface-visibility: hidden;*/ } </style> </head> <body> <div class="box"> <div class="di"> <div class="z"> <p>可以拖拽图片文件进来</p> <p>可以拖拽图片文件进来</p> <p>可以拖拽图片文件进来</p> <p>可以拖拽图片文件进来</p> <p>可以拖拽图片文件进来</p> <p>可以拖拽图片文件进来</p> <p>可以拖拽图片文件进来</p> <p>可以拖拽图片文件进来</p> <p>可以拖拽图片文件进来</p> <p>可以拖拽图片文件进来</p> <p>可以拖拽图片文件进来</p> <p>可以拖拽图片文件进来</p> </div> </div> </div> <script> window.onload = function() { //声明变量s为鼠标移动距离和改变角度的比例 var s = 15; var zbox = document.querySelector('.z'); var dibox = document.querySelector('.di'); var ps = document.querySelectorAll('p'); //声明鼠标移动速度的两个变量 var timer = null; var xs = 0; var ys = 0; //给每个p标签设置位置 for(var i = 0; i < ps.length; i++) { ps[i].style.transform = 'rotatex(-90deg) rotatey(' + i * 30 + 'deg) translatez(400px)' } //鼠标按下 window.onmousedown = function(e) { //清除旋转盒子过渡属性 dibox.style.transition = ''; zbox.style.transition = ''; // $(dibox).css('transition', ''); // $(zbox).css('transition', ''); //声明计算速度用的初始时间tc,初始值等于鼠标按下时的时间 var tc = new Date().getTime(); //声明t,初始值和按下时间相同 var t = tc; //每隔2毫秒更新现在时间t timer = setInterval(function() { t = new Date().getTime(); }, 2) //取得按下时的坐标 var xc = e.clientX; var yc = e.clientY; //声明计算速度用的初始坐标初始值等于按下时的坐标 var xsc = xc; var ysc = yc; //获取3d状态矩阵,处理后得到x轴旋转角度(0~180度之间) var cc = window.getComputedStyle(dibox).transform; // var cc = $(dibox).css('transform'); cc = cc.split(','); cc = Math.acos(cc[5]) / Math.PI * 180; //调用函数得到z轴旋转角度(把matrix()矩阵转换为旋转角度) var zc = getz(zbox); //绑定鼠标移动事件 window.onmousemove = function(e) { var x = e.clientX; var y = e.clientY; //如果现在时间和初始时间差值大于10 //计算一下鼠标速度xs;ys //重新赋值用来计算速度的初始时间和初始坐标 if(t - tc > 10) { xs = (x - xsc) / (t - tc) * 15; xs = xs; ys = (y - ysc) / (t - tc) * 10; ys = ys / 2; tc = t; xsc = x; ysc = y; } //根据鼠标y坐标移动距离计算新的x轴旋转角度并设置给盒子样式,范围1~179 //如果转到0或者180时,就完全看不到了,而且获取到的矩阵就变成了6个参数的,下次在拖动时,转换成角度的方法就不适用了 var c = cc - (y - yc) / s > 179 ? 179 : (cc - (y - yc) / s < 1 ? 1 : cc - (y - yc) / s); dibox.style.transform = 'translate(-50%, -50%) rotatex(' + c + 'deg)'; // $(dibox).css('transform', 'translate(-50%, -50%) rotatex(' + c + 'deg)') //根据鼠标x坐标移动距离计算新的z轴旋转角度并设置给盒子样式 var z = zc - (x - xc) / s; //如果角度大于不在0-360度间,取0-360之间对应的值 z = z > 360 ? z - 360 : z < 0 ? 360 + z : z; //测试发现在0.5附近和359.48以上时获取角度的函数会出错(可能是因为js对于小数计算不精确的原因,计算角度时要用三角函数),直接跳过这一段 if(z > 359.48) { z = 359.48; } if(z < 0.51 && z > 0.49) { z = 0.49; } zbox.style.transform = 'rotatez(' + z + 'deg)'; } } //鼠标松开 window.onmouseup = function() { levae(); } //鼠标松开事件封装函数 function levae() { //清除用来计算速度时用的计时器,移除鼠标移动事件 clearInterval(timer); window.onmousemove = null; if(ys !== 0 || xs !== 0) { //获取到x轴旋转的角度 var cc = window.getComputedStyle(dibox).transform; cc = cc.split(','); cc = Math.acos(cc[5]) / Math.PI * 180; //获取到z轴旋转的角度 var zc = getz(zbox); //结合速度算出目标状态 var c = cc - (ys) > 179 ? 179 : (cc - (ys) < 1 ? 1 : cc - (ys)); var z = zc - (xs); //避免跨越0度和360度 z = z > 359.48 ? 359.48 : z < 0 ? 0 : z; if(z < 0.51 && z > 0.49) { z = 0.49; } //速度重置为0 ys = 0; xs = 0; //设置给盒子目标状态并且加上过渡属性 dibox.style.transform = 'translate(-50%, -50%) rotatex(' + c + 'deg)'; dibox.style.transition = 'transform 0.5s ease-out'; zbox.style.transform = 'rotatez(' + z + 'deg)'; zbox.style.transition = 'transform 0.5s ease-out'; } } //获取z轴旋转角度函数 //引用自:http://blog.csdn.net/u010884130/article/details/50005437 /* * 解析matrix矩阵,0°-360°,返回旋转角度 * 当a=b||-a=b,0<=deg<=180 * 当-a+b=180,180<=deg<=270 * 当a+b=180,270<=deg<=360 * * 当0<=deg<=180,deg=d; * 当180<deg<=270,deg=180+c; * 当270<deg<=360,deg=360-(c||d); * */ function getz(element) { var c = window.getComputedStyle(element).transform; c = c.split('(')[1].split(','); var aa = Math.round(Math.asin(c[0]) / Math.PI * 180); var bb = Math.round(Math.acos(c[1]) / Math.PI * 180); var cc = Math.round(Math.asin(c[2]) / Math.PI * 180); var dd = Math.round(Math.acos(c[3]) / Math.PI * 180); var deg = 0; if(aa == bb || -aa == bb) { deg = Math.acos(c[3]) / Math.PI * 180; } else if(-aa + bb == 180) { deg = 180 + Math.asin(c[2]) / Math.PI * 180; } else if(aa + bb == 180) { deg = (360 - Math.asin(c[2]) / Math.PI * 180) || (360 - Math.acos(c[3]) / Math.PI * 180); } return deg >= 360 ? 0 : deg; //return (aa+','+bb+','+cc+','+dd); } //取消鼠标拖拽文件进入窗口的默认行为 window.ondragover = function(e) { e.preventDefault(); } window.ondrop = function(e) { e.preventDefault(); } //添加拖拽到p标签的事件 for(var i = 0; i < ps.length; i++) { ps[i].index = i; ps[i].ondrop = function(e){ var w = new FileReader(); w.index = this.index; w.readAsDataURL(e.dataTransfer.files[0]); w.onload = function(){ ps[this.index].style.backgroundImage = 'url('+w.result+')'; ps[this.index].innerHTML = ''; } } } } </script> </body> </html>