在之前的文章js调用陀螺仪配合CSS3 3D实现VR效果(一)中,介绍了用css3搭建了3d正方体,并控制正方体运动,模拟3d环境中视角的变化。这种方式的本质是"观测者不动,世界在动",观测者就是网页浏览器可视区,世界就是网页的内容。
结合之前的内容,我们可以用六张照片完成一个正方体空间,达到模拟“世界”的作用,让用户看到一个全景的空间。六张图片和全景图的关系,大家可以了解一下“天空盒”的概念。只要有设备能拍摄全景图,有很多软件能将全景图生成为“天空盒”模式的六张图片,以便实现VR全景网页的效果。本文介绍的方式本质还是应用dom来实现空间,当然也有通过canvas+three.js的方式实现全景,但是webgl在移动端的性能和兼容性还有待提高,不过从页面渲染的性能来看,dom的实现方式是差一些。dom天空盒实现如下:
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<style type="text/css">
*{ padding:0; margin:0;touch-action: none;}
body,html{height: 100%;background:#ccc;}
#box{ width: 100%;
height: 100%;
border: 1px solid #000;
-webkit-perspective: 200px;
perspective: 200px;
overflow: hidden;}
#z{ position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-transform: translateZ(200px);
transform: translateZ(200px);}
#div{width:1024px; height:1024px; -webkit-transform-style: preserve-3d;transform-style:preserve-3d;-webkit-transform: translateZ(-512px) rotateY(0deg);transform: translateZ(-512px) rotateY(0deg); text-align: center;position: absolute;left:50%;top:50%; margin: -512px 0 0 -512px;}
#div span{ width:100%; height:100%; position:absolute;left:0;top:0;-webkit-backface-visibility: hidden;backface-visibility: hidden;touch-action: none;}
#div div{ touch-action: none;}
#div span:nth-of-type(1){
background-image: url(//res.nuoyal.com/skybox/mobile_f.jpg);
perspective: 400px;
-webkit-transform: rotateY(0deg) translateZ(-512px);
transform: rotateY(0deg) translateZ(-512px);
}
#div span:nth-of-type(2){
background-image: url(//res.nuoyal.com/skybox/mobile_r.jpg);
-webkit-transform: rotateY(-90deg) translateZ(-512px);
transform: rotateY(-90deg) translateZ(-512px);
}
#div span:nth-of-type(3){
background-image: url(//res.nuoyal.com/skybox/mobile_b.jpg);
-webkit-transform: rotateY(-180deg) translateZ(-512px);
transform: rotateY(-180deg) translateZ(-512px);
}
#div span:nth-of-type(4){
background-image: url(//res.nuoyal.com/skybox/mobile_l.jpg);
-webkit-transform: rotateY(-270deg) translateZ(-512px);
transform: rotateY(-270deg) translateZ(-512px);
}
#div span:nth-of-type(5){
background-image: url(//res.nuoyal.com/skybox/mobile_u.jpg);
-webkit-transform: rotateX(-90deg) translateZ(-512px);
transform: rotateX(-90deg) translateZ(-512px);
}
#div span:nth-of-type(6){
background-image: url(//res.nuoyal.com/skybox/mobile_d.jpg);
-webkit-transform: rotateX(90deg) translateZ(-512px);
transform: rotateX(90deg) translateZ(-512px);
}
#div span{ background-position: 0 0; background-repeat: no-repeat;}
</style>
<script type="text/javascript">
function setRem(psnum){
var html = document.documentElement;
html.style.fontSize = html.clientWidth / psnum +"px";
}
setRem(7.5);
window.addEventListener('orientation' in window?"deviceorientation":"resize",function(e){
setRem(7.5);
});
</script>
</head>
<body id="main">
<div id="box">
<div id="z" style="transform: translateZ(300px);">
<div id="div" style="transform: rotateX(0deg) rotateY(0deg);">
<span style="transform: rotateY(0deg) translateZ(-512px);"></span>
<span style="transform: rotateY(-90deg) translateZ(-512px);"></span>
<span style="transform: rotateY(-180deg) translateZ(-512px);"></span>
<span style="transform: rotateY(-270deg) translateZ(-512px);"></span>
<span style="transform: rotateX(-90deg) translateZ(-512px);"></span>
<span style="transform: rotateX(90deg) translateZ(-512px);"></span>
</div>
</div>
</div>
<script type="text/javascript">
function cssTransform(el, attr, val) {
if(!el.transform){
el.transform = {}
}
if(typeof val == "undefined"){
if(typeof el.transform[attr] == "undefined"){
switch(attr) {
case "scale":
case "scaleX":
case "scaleY":
el.transform[attr] = 100;
break;
default:
el.transform[attr] = 0;
}
}
return el.transform[attr];
} else {
var transformVal = "";
el.transform[attr] = Number(val);
for(var s in el.transform){
switch(s){
case "rotate":
case "rotateX":
case "rotateY":
case "rotateZ":
case "skewX":
case "skewY":
transformVal += " "+s+"("+el.transform[s]+"deg)";
break;
case "translateX":
case "translateY":
case "translateZ":
transformVal += " "+s+"("+el.transform[s]+"px)";
break;
case "scale":
case "scaleX":
case "scaleY":
transformVal += " "+s+"("+el.transform[s]/100+")";
break;
}
}
el.style.WebkitTransform = el.style.transform = transformVal;
}
}
function css(el, attr , val){
if(attr == "rotate" || attr == "rotateX"
|| attr == "rotateY" ||attr == "rotateZ"
|| attr == "scale" || attr == "scaleX"
|| attr == "scaleY" || attr == "skewX"
|| attr == "skewY" || attr == "translateX"
|| attr == "translateY" || attr == "translateZ" ){
return cssTransform(el, attr, val);
}
if(arguments.length == 2){
var val = getComputedStyle(el)[attr];
if(attr=='opacity'){
val = Math.round(val*100);
}
return parseFloat(val);
}
if(attr == "opacity") {
el.style.opacity= val/100;
} else {
el.style[attr]= val + "px";
}
}
//全局变量
var tdis = {}; //当前移动的值
var lastdis = {}; //上次移动的值
var oZ = document.querySelector('#z');
var oDiv = document.querySelector('#div');
var isTouch = false;
var isStart = false;
(function(){
var box = document.querySelector('#box');
var startPoint = {x:0,y:0};
var startDeg = {x:0,y:0};
var scale = 1024/90;
var nowPo = {x:0,y:0};
var last = {x:0,y:0};
var lastDis = {x:0,y:0};
box.addEventListener('touchstart',function(e){
var oDiv = document.querySelector('#div');
isTouch = true;
isStart = false;
tdis = {};
lastdis = {};
startPoint.x = e.changedTouches[0].pageX;
startPoint.y = e.changedTouches[0].pageY;
startDeg.x = css(oDiv,"rotateY");
startDeg.y = css(oDiv,"rotateX");
css(oDiv,"rotateY",startDeg.x);
css(oDiv,"rotateX",startDeg.y);
last.x = startDeg.x;
last.y = startDeg.y;
lastDis.x = 0;
lastDis.y = 0;
lastTime = new Date().getTime();
lastTimeDis = 0;
});
box.addEventListener('touchmove',function(e){
var oDiv = document.querySelector('#div');
isTouch = true;
isStart = false;
tdis = {};
lastdis = {};
var dis = {};
var nowTime = new Date().getTime();
dis.x = e.changedTouches[0].pageX - startPoint.x;
dis.y = e.changedTouches[0].pageY - startPoint.y;
var degX = startDeg.x-dis.x/scale;
var degY = startDeg.y+dis.y/scale;
if(degY > 60){
degY = 60;
}else if(degY < -60){
degY = -60;
}
css(oDiv,"rotateY",degX);
css(oDiv,"rotateX",degY);
lastDis.x = (startDeg.x + dis.x) - last.x;
lastDis.y = (startDeg.y + dis.y) - last.y;
last.x = startDeg.x + dis.x;
last.y = startDeg.y + dis.y;
lastTimeDis = nowTime - lastTime;
lastTime = nowTime;
});
box.addEventListener('touchend',function(e){
var oDiv = document.querySelector('#div');
if(window.isStart){
return false;
}
isTouch = false;
isStart = false;
var speedX = lastDis.x/lastTimeDis;
var speedY = lastDis.y/lastTimeDis;
var disX = parseInt(speedX * 20);
var disY = parseInt(speedY * 20);
disX = isNaN(disX)?0:disX;
disY = isNaN(disY)?0:disY;
var targetX = css(oDiv,"rotateY")-disX;
var targetY = css(oDiv,"rotateX")+disY;
if(targetY > 60){
targetY = 60;
}else if(targetY < -60){
targetY = -60;
}
});
})();
</script>
</body>
运行测试上面代码 --移动端运行或浏览器开启移动端模拟器后手指滑动
网页运行时,通过touch方法控制正方体的transform旋转。针对手指滑动方向,可以控制正方体的旋转方向,实现VR的空间。若将控制正方体旋转的方式由滑屏升级为成陀螺仪,则最终实现VR效果。陀螺仪的实现如下:
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<style type="text/css">
*{ padding:0; margin:0;touch-action: none;}
body,html{height: 100%;background:#ccc;}
#box{ width: 100%;
height: 100%;
border: 1px solid #000;
-webkit-perspective: 200px;
perspective: 200px;
overflow: hidden;}
#z{ position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-transform: translateZ(200px);
transform: translateZ(200px);}
#div{width:1024px; height:1024px; -webkit-transform-style: preserve-3d;transform-style:preserve-3d;-webkit-transform: translateZ(-512px) rotateY(0deg);transform: translateZ(-512px) rotateY(0deg); text-align: center;position: absolute;left:50%;top:50%; margin: -512px 0 0 -512px;}
#div span{ width:100%; height:100%; position:absolute;left:0;top:0;-webkit-backface-visibility: hidden;backface-visibility: hidden;touch-action: none;}
#div div{ touch-action: none;}
#div span:nth-of-type(1){
background-image: url(http://res.nuoyal.com/skybox/mobile_f.jpg);
perspective: 400px;
-webkit-transform: rotateY(0deg) translateZ(-512px);
transform: rotateY(0deg) translateZ(-512px);
}
#div span:nth-of-type(2){
background-image: url(http://res.nuoyal.com/skybox/mobile_r.jpg);
-webkit-transform: rotateY(-90deg) translateZ(-512px);
transform: rotateY(-90deg) translateZ(-512px);
}
#div span:nth-of-type(3){
background-image: url(http://res.nuoyal.com/skybox/mobile_b.jpg);
-webkit-transform: rotateY(-180deg) translateZ(-512px);
transform: rotateY(-180deg) translateZ(-512px);
}
#div span:nth-of-type(4){
background-image: url(http://res.nuoyal.com/skybox/mobile_l.jpg);
-webkit-transform: rotateY(-270deg) translateZ(-512px);
transform: rotateY(-270deg) translateZ(-512px);
}
#div span:nth-of-type(5){
background-image: url(http://res.nuoyal.com/skybox/mobile_u.jpg);
-webkit-transform: rotateX(-90deg) translateZ(-512px);
transform: rotateX(-90deg) translateZ(-512px);
}
#div span:nth-of-type(6){
background-image: url(http://res.nuoyal.com/skybox/mobile_d.jpg);
-webkit-transform: rotateX(90deg) translateZ(-512px);
transform: rotateX(90deg) translateZ(-512px);
}
#div span{ background-position: 0 0; background-repeat: no-repeat;}
</style>
<script type="text/javascript">
function setRem(psnum){
var html = document.documentElement;
html.style.fontSize = html.clientWidth / psnum +"px";
}
setRem(7.5);
window.addEventListener('orientation' in window?"deviceorientation":"resize",function(e){
setRem(7.5);
});
</script>
</head>
<body id="main">
<div id="box">
<div id="z" style="transform: translateZ(300px);">
<div id="div" style="transform: rotateX(0deg) rotateY(0deg);">
<span style="transform: rotateY(0deg) translateZ(-512px);"></span>
<span style="transform: rotateY(-90deg) translateZ(-512px);"></span>
<span style="transform: rotateY(-180deg) translateZ(-512px);"></span>
<span style="transform: rotateY(-270deg) translateZ(-512px);"></span>
<span style="transform: rotateX(-90deg) translateZ(-512px);"></span>
<span style="transform: rotateX(90deg) translateZ(-512px);"></span>
</div>
</div>
</div>
<script type="text/javascript" src="https://res.nuoyal.com/h53d/js/m.Tween.js"></script>
<script type="text/javascript">
//全局变量
var tdis = {}; //当前移动的值
var lastdis = {}; //上次移动的值
var oZ = document.querySelector('#z');
var oDiv = document.querySelector('#div');
var isTouch = false;
var isStart = false;
//陀螺仪
function setSensors(){
var start = {}; //首次触发,开启陀螺仪时的值
var now = {}; //触发旋转后的当前值
var startEl = {};//首次触发,开启旋转时的元素的角度值
var lastTime = Date.now();
var dir = window.orientation; //检测横竖屏
var rooy = css(oDiv,"rotateY");
var roox = css(oDiv,"rotateX");
var timertl = null;
isTouch = false;
isStart = false; //是否开始触发陀螺仪
window.addEventListener("orientationchange", function(e) {
var oDiv = document.querySelector('#div');
oZ.removeChild(oDiv);
ohtml = oDiv.innerHTML;
var oniv=document.createElement('div');
oniv.id = "div";
oniv.innerHTML = ohtml;
dir = window.orientation;
tdis = {}; //陀螺仪当前移动的值
lastdis = {}; //陀螺仪上次移动的值
isTouch = false;
isStart = false;
start = {x:0,y:0};
now = {x:0,y:0};
startEl = {x:0,y:0};
lastTime = Date.now();
css(oniv,"rotateX",0);
css(oniv,"rotateY",0);
rooy = 0;
roox = 0;
oZ.appendChild(oniv);
});
window.addEventListener('deviceorientation',function(e){
var oDiv = document.querySelector('#div');
if(window.isTouch){
return false;
}
var alpha = Math.round(e.alpha);
switch(dir){
case 0:
now.x = e.beta; //当前的旋转角度
now.y = e.gamma+alpha;
break;
case 90:
now.x = -e.gamma;
now.y = e.beta+alpha;
break;
case -90:
now.x = e.gamma;
now.y = (e.beta+alpha);
break;
case 180:
now.x = -e.beta;
now.y = -(e.gamma+alpha);
break;
}
var nowTime = Date.now();
if((nowTime-lastTime)<130){
return false;
}
lastTime = nowTime;
if(!isStart){
if(!isTouch){
isStart = true;
//start
start.x = now.x;
start.y = now.y;
startEl.x = css(oDiv,"rotateX");
startEl.y = css(oDiv,"rotateY");
rooy = css(oDiv,"rotateY");
}
}else{
if(isTouch){
return false;
}
isTouch = false;
lastdis.x = tdis.x;
lastdis.y = tdis.y;
tdis.x = now.x - start.x;
tdis.y = now.y - start.y;
var tag = {};
var lastag = {};
tag.x = startEl.x + tdis.x;
rooy = css(oDiv,"rotateY");
roox = css(oDiv,"rotateX");
var opath = tdis.y-lastdis.y;
var opathx = tdis.x-lastdis.x;
if(dir == 90 || dir == -90){
if(opathx>0){
if(Math.abs(opathx)>140){
opathx = opathx-180;
}
}else if(opathx<0){
if(Math.abs(opathx)>140){
opathx = 180-Math.abs(opathx);
}
}else{
opathx = 0;
}
roox +=(opathx*2);
}
if(opath>0){
if(Math.abs(opath)>200){
opath = opath-360;
}
}else if(opath<0){
if(Math.abs(opath)>200){
opath = 360-Math.abs(opath);
}
}else{
opath = 0;
}
rooy +=-(opath*2);
if(dir == 90 || dir == -90){
MTween({
el:oDiv,
target:{rotateX:roox,rotateY:rooy},
time:400,
type:"easeOut",
callBack:function(){
isTouch = false;
}
});
}else{
MTween({
el:oDiv,
target:{rotateX:tag.x,rotateY:rooy},
time:400,
type:"easeOut",
callBack:function(){
isTouch = false;
}
});
}
}
});
}
setSensors();
</script>
</body>
运行测试上面代码 --移动端运行或浏览器开启移动端模拟器后手机晃动
陀螺仪的控制和手指滑动控制可以结合起来共存使用,使操作更顺滑。
点赞 (0)
欢迎转载:转载时请注明本文出处及文章链接