2016年淘宝发出一封造物节的H5邀请函,风靡朋友圈.我们发现不需要通过建模、webgl等技术,仅通过css3与js我们也能实现3D场景与视角.我们经常使用css3中的transform对元素进行3d变换,实现一些长方体等.就从这个角度出发,我们一点点实现3D场景.
第1步:先用css3制作长方体
dom元素在做transform变形时是按照x,y,z轴来做参照,初始状态元素做rotate旋转时,默认以z轴为中心旋转.z轴垂直于屏幕,如下图:
为了方便查看3d效果,我们先准备一个容器,并且给它设置三个属性:
1.先设置容器transform-style:preserve-3d,表示容器内部以3D空间呈现。
2.再设置容器的perspective:100px;(景深),可以理解为用户和屏幕的距离,初始时元素位置是紧贴屏幕的.若元素此时往translateZ负方向移动,则看到的元素越来越小。若元素往translateZ正方向移动,则看到的元素越来越大,当这个正值等于我们设置的perspective时,元素将消失,此时的情况就是元素运动到和我们的视点同一平面,视线刚好穿过了元素。
3.还需要设置容器的perspective-origin:10px 10px;(视点),默认我们从元素的正中心看向它,设置视点就是设置我们从哪个点看向元素。
为了制作正方体,我们在容器内准备六个颜色不同的平面.初始时6个平面是重叠的,只能看到一个平面,我们旋转并移动第1个平面,让它和初始的平面垂直,并且设置我们的视点从容器右下方看向容器(注意transform执行时写在后面的先执行)
<style type="text/css">
#box {position: relative;width: 10px;height: 10px;margin: 200px auto;transform-style:preserve-3d;perspective:300px;perspective-origin:100px 200px;}
#box div {position: absolute;left: 50%;top: 50%;margin: -50px 0 0 -50px;width: 100px;height: 100px;text-align: center;font-size: 26px; line-height: 100px;}
#box div:nth-of-type(1) {background: yellow;transform:translateX(-50px) translateZ(-50px) rotateY(-90deg); }
#box div:nth-of-type(2) {background: red;}
#box div:nth-of-type(3) {background: gray; }
#box div:nth-of-type(4) {background: blue; }
#box div:nth-of-type(5) {background: #FFCC00;}
#box div:nth-of-type(6) {background: green;}
</style>
</head>
<body>
<div id="box">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</div>
运行测试上面代码 此时能看到其中两个面
接着我们依次transform其余四个面,让6个平面拼成正方体,且还是在右下方的视角看向正方体.(注意控制角度使元素正面朝外)
<style type="text/css">
#box {position: relative;width: 10px;height: 10px;margin: 200px auto;transform-style:preserve-3d;perspective:300px;perspective-origin:100px 200px;}
#box div {position: absolute;left: 50%;top: 50%;margin: -50px 0 0 -50px;width: 100px;height: 100px;text-align: center;font-size: 26px; line-height: 100px;}
#box div:nth-of-type(1) {background: yellow;transform:translateX(-50px) translateZ(-50px) rotateY(-90deg);} /*左面*/
#box div:nth-of-type(2) {background: red;transform:translateZ(-50px) translateY(-50px) rotateX(90deg);} /*上面*/
#box div:nth-of-type(3) {background: gray;transform:translateZ(-50px) translateY(50px) rotateX(-90deg); } /*下面*/
#box div:nth-of-type(4) {background: blue;transform:translateX(50px) translateZ(-50px) rotateY(90deg);} /*右面*/
#box div:nth-of-type(5) {background: #FFCC00;transform:translateZ(-100px) rotateY(180deg);} /*后面*/
#box div:nth-of-type(6) {background: green;} /*前面,初始面*/
</style>
</head>
<body>
<div id="box">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</div>
运行测试上面代码 此时从右下角看,能看到3个面,此时正方体已经形成.
第2步:正方体内部视角的制作和修正
此时我们尝试让正方体一直向我们的方向移动.我们需要再套一个容器方便整体移动,并以中心点为基准.当新容器的translateZ超过我们设置的景深值时,就像上面提到的,我们的视线穿透了初始面上的元素,从而看到了正方体的内部。
<style type="text/css">
#box {position: relative;width: 10px;height: 10px;margin: 300px auto;transform-style:preserve-3d;perspective:300px;}
#box div {position: absolute;left: 50%;top: 50%;margin: -50px 0 0 -50px;width: 100px;height: 100px;text-align: center;font-size: 26px; line-height: 100px;}
#wrap{ display: block; transform:translateZ(240px);transform-style:preserve-3d;perspective:300px; }
#box div:nth-of-type(1) {background: yellow;transform:translateX(-50px) translateZ(-50px) rotateY(-90deg);}/*左面*/
#box div:nth-of-type(2) {background: red;transform:translateZ(-50px) translateY(-50px) rotateX(90deg);} /*上面*/
#box div:nth-of-type(3) {background: gray;transform:translateZ(-50px) translateY(50px) rotateX(-90deg); } /*下面*/
#box div:nth-of-type(4) {background: blue;transform:translateX(50px) translateZ(-50px) rotateY(90deg);} /*右面*/
#box div:nth-of-type(5) {background: #FFCC00;transform:translateZ(-100px) rotateY(180deg);} /*后面*/
#box div:nth-of-type(6) {background: green;} /*前面,初始面*/
a{ position: relative; z-index: 10;}
</style>
</head>
<body>
<a href="javascript:;">点击+++</a>
<a href="javascript:;">点击---</a>
<div id="box">
<span id="wrap">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</span>
</div>
<script type="text/javascript">
var aA = document.querySelectorAll("a");
var oWrap = document.getElementById("wrap");
var iNow = 240;
aA[0].onclick = function(){
iNow+=20;
oWrap.style.webkitTransform = "translateZ("+iNow+"px)"
}
aA[1].onclick = function(){
iNow-=20;
oWrap.style.webkitTransform = "translateZ("+iNow+"px)"
}
</script>
我们已经可以控制视角进出一个盒子内部了.但此时进入盒子后看到的是元素的背面,是反向的.接下来我们设置backface-visibility:hidden;隐藏元素背面,此时元素只有正面可以被看到,从元素背面是看不到元素的.当进入正方体内时,由于正方体内部显示的全部是元素背面,所以元素都隐藏了.
<style type="text/css">
#box {position: relative;width: 10px;height: 10px;margin: 300px auto;transform-style:preserve-3d;perspective:300px;perspective-origin:100px 0;}
#box div {position: absolute;left: 50%;top: 50%;margin: -50px 0 0 -50px;width: 100px;height: 100px;text-align: center;font-size: 26px; line-height: 100px; backface-visibility: hidden;}
#wrap{ display: block; transform:translateZ(240px);transform-style:preserve-3d;perspective:300px; }
#box div:nth-of-type(1) {background: yellow;transform:translateX(-50px) translateZ(-50px) rotateY(-90deg);}/*左面*/
#box div:nth-of-type(2) {background: red;transform:translateZ(-50px) translateY(-50px) rotateX(90deg);} /*上面*/
#box div:nth-of-type(3) {background: gray;transform:translateZ(-50px) translateY(50px) rotateX(-90deg); } /*下面*/
#box div:nth-of-type(4) {background: blue;transform:translateX(50px) translateZ(-50px) rotateY(90deg);} /*右面*/
#box div:nth-of-type(5) {background: #FFCC00;transform:translateZ(-100px) rotateY(180deg);} /*后面*/
#box div:nth-of-type(6) {background: green;} /*前面,初始面*/
a{ position: relative; z-index: 10;}
</style>
</head>
<body>
<a href="javascript:;">点击+++</a>
<a href="javascript:;">点击---</a>
<a href="javascript:;">视点左移</a>
<a href="javascript:;">视点右移</a>
<div id="box">
<span id="wrap">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</span>
</div>
<script type="text/javascript">
var aA = document.querySelectorAll("a");
var oWrap = document.getElementById("wrap");
var oBox = document.getElementById("box");
var iNow = 240;
var iDeg = 0;
aA[0].onclick = function(){
iNow+=20;
oWrap.style.webkitTransform = "translateZ("+iNow+"px)"
}
aA[1].onclick = function(){
iNow-=20;
oWrap.style.webkitTransform = "translateZ("+iNow+"px)"
}
aA[2].onclick = function(){
iDeg+=10;
oBox.style.webkitTransform = "rotateY("+iDeg+"deg)"
}
aA[3].onclick = function(){
iDeg-=10;
oBox.style.webkitTransform = "rotateY("+iDeg+"deg)"
}
</script>
接着我们让每两个对立的面互换位置,以此保证了每个面外正面朝内,背面朝外,这样可以看到正方体内部的面,外部的面看不到.
<style type="text/css">
#box {position: relative;width: 10px;height: 10px;margin: 300px auto;transform-style:preserve-3d;perspective:300px;perspective-origin:100px 0;}
#box div {position: absolute;left: 50%;top: 50%;margin: -50px 0 0 -50px;width: 100px;height: 100px;text-align: center;font-size: 26px; line-height: 100px; backface-visibility: hidden;}
#wrap{ display: block; transform:translateZ(240px);transform-style:preserve-3d;perspective:300px; }
#box div:nth-of-type(1) {background: yellow;transform:translateX(50px) translateZ(-50px) rotateY(-90deg);} /*左面*/
#box div:nth-of-type(2) {background: red;transform:translateZ(-50px) translateY(50px) rotateX(90deg);} /*上面*/
#box div:nth-of-type(3) {background: gray;transform:translateZ(-50px) translateY(-50px) rotateX(-90deg); } /*下面*/
#box div:nth-of-type(4) {background: blue;transform:translateX(-50px) translateZ(-50px) rotateY(90deg);} /*右面*/
#box div:nth-of-type(5) {background: #FFCC00;transform:rotateY(180deg);} /*后面*/
#box div:nth-of-type(6) {background: green;transform:translateZ(-100px)} /*前面,初始面*/
a{ position: relative; z-index: 10; color: #000;}
</style>
</head>
<body>
<a href="javascript:;">点击+++</a>
<a href="javascript:;">点击---</a>
<a href="javascript:;">视点左移</a>
<a href="javascript:;">视点右移</a>
<div id="box">
<span id="wrap">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</span>
</div>
<script type="text/javascript">
var aA = document.querySelectorAll("a");
var oWrap = document.getElementById("wrap");
var oBox = document.getElementById("box");
var iNow = 240;
var iDeg = 0;
aA[0].onclick = function(){
iNow+=20;
oWrap.style.webkitTransform = "translateZ("+iNow+"px)"
}
aA[1].onclick = function(){
iNow-=20;
oWrap.style.webkitTransform = "translateZ("+iNow+"px)"
}
aA[2].onclick = function(){
iDeg+=10;
oBox.style.webkitTransform = "rotateY("+iDeg+"deg)"
}
aA[3].onclick = function(){
iDeg-=10;
oBox.style.webkitTransform = "rotateY("+iDeg+"deg)"
}
</script>
此时我们便可以在正方体中看到所有正面,并且方便调试了.为了模拟3D视角,我们把视角固定在正方体的中心,并且让正方体以自身中心点旋转,我们便可以浏览这个小空间了.transform-origin:50% 50% 100px;用来设置元素运动的中心点.
<style type="text/css">
#box {position: relative;width: 10px;height: 10px;margin: 300px auto;transform-style:preserve-3d;perspective:300px;}
#box div {position: absolute;left: 50%;top: 50%;margin: -50px 0 0 -50px;width: 100px;height: 100px;text-align: center;font-size: 26px; line-height: 100px; backface-visibility: hidden;}
#wrap{ display: block; transform:rotateY(0deg) translateZ(350px);transform-style:preserve-3d;perspective:300px;transform-origin: 50% 50% 300px;}
#box div:nth-of-type(1) {background: yellow;transform:translateX(50px) translateZ(-50px) rotateY(-90deg);} /*左面*/
#box div:nth-of-type(2) {background: red;transform:translateZ(-50px) translateY(50px) rotateX(90deg);} /*上面*/
#box div:nth-of-type(3) {background: gray;transform:translateZ(-50px) translateY(-50px) rotateX(-90deg); } /*下面*/
#box div:nth-of-type(4) {background: blue;transform:translateX(-50px) translateZ(-50px) rotateY(90deg);} /*右面*/
#box div:nth-of-type(5) {background: #FFCC00;transform:rotateY(180deg);} /*后面*/
#box div:nth-of-type(6) {background: green;transform:translateZ(-100px)} /*前面,初始面*/
a{ position: relative; z-index: 20; color: #000;}
a:link,a:visited,a:hover,a:active{color: #000;}
</style>
</head>
<body>
<p style="text-align:center;">
<a href="javascript:;">点击+++</a>
<a href="javascript:;">点击---</a>
<a href="javascript:;">视点左移</a>
<a href="javascript:;">视点右移</a>
<a href="javascript:;">左转</a>
<a href="javascript:;">右转</a>
</p>
<div id="box">
<span id="wrap">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</span>
</div>
<script type="text/javascript">
var aA = document.querySelectorAll("a");
var oWrap = document.getElementById("wrap");
var oBox = document.getElementById("box");
var iNow = 240;
var iDeg = 0;
var yDeg = 0;
var xDeg = 0;
aA[0].onclick = function(){
iNow+=20;
oWrap.style.webkitTransform = "translateZ("+iNow+"px)"
}
aA[1].onclick = function(){
iNow-=20;
oWrap.style.webkitTransform = "translateZ("+iNow+"px)"
}
aA[2].onclick = function(){
iDeg+=10;
oBox.style.webkitTransform = "rotateY("+iDeg+"deg)"
}
aA[3].onclick = function(){
iDeg-=10;
oBox.style.webkitTransform = "rotateY("+iDeg+"deg)"
}
aA[4].onclick = function(){
yDeg+=5;
oWrap.style.webkitTransform = "rotateY("+yDeg+"deg) translateZ(350px)"
}
aA[5].onclick = function(){
yDeg-=5;
oWrap.style.webkitTransform = "rotateY("+yDeg+"deg) translateZ(350px)"
}
</script>
最终我们制作了一个正方体,并且可以浏览它内部的空间.当然为了兼容更多视角角度的移动,还需要做很多兼容处理.在此基础上我们加入touch操作,封装动画,再用全景照片替换6个面上的文字,可以实现全景看房的效果.点击查看Demo(移动端)
欢迎转载:转载时请注明本文出处及文章链接