第一次看到只用 CSS 就可以畫 3D 物件覺得非常神奇, 想當初在設計導航程式時,在那記憶體只有 16M 的機器上,要跑全台灣/全中國地圖,而且導航除了需要旋轉地圖到行進方向,最好還要有 2.5D 的斜角視圖才可以看到遠方更多的資訊,當時所有計算全部自己寫,光旋轉就要一堆 sin , cos 的計算,而現在只要一行指令就可以旋轉了......
3D 的基礎,投影面和視點, 在 CSS 上也必須分層建立好
首先先建一個 view 裡面指定投影距離 perspective:400px; 在 view 裡頭建立 camera , 這裡有個很重要的參數 transform-style: preserve-3d; 然後在 camera 裡再建立 obj , 同樣的這裡也必須加上 transform-style: preserve-3d; ,然後就可以在 obj 中放面的資料
最簡單的範例如下 :
<style>
#a06040
{
width:300px;
height:300px;
}
#a06040 .view
{
position:absolute;
perspective:400px;
width:300px;
height:300px;
perspective-origin:50% 50%;
}
#a06040 .camera
{
position:absolute;
width:300px;
height:300px;
transform-style: preserve-3d;
}
#a06040 .obj
{
position:absolute;
width:300px;
height:300px;
transform-style: preserve-3d;
transform:translate3d(0px,0px,-400px);
}
#a06040 .aa
{
position:absolute;
width:300px;
height:300px;
background-color:#aaaaff;
}
#a06040 .aa1
{
transform:translate3d(150px,0,0) rotate3d(0,1,0,-60deg);
}
#a06040 .aa2
{
transform:translate3d(-150px,0,0) rotate3d(0,1,0,60deg);
}
</style>
<div id="a06040">
<div class="view">
<div class="camera">
<div class="obj">
<div class="aa aa1"></div>
<div class="aa aa2"></div>
</div>
</div>
</div>
</div>
如果只有一個物件,可以省略 obj 那一層,也就是每個面都是一個物件的概念, 例如以下範例,利用八個面組成一顆鑽石
<style>
#a06043 .view
{
font-size:6em;
position:relative;
perspective:3em;
width:2em;
height:2em;
perspective-origin:50% 50%;
transform:scaley(1);
border:1px solid #000;
}
#a06043 .camera
{
position:absolute;
width:2em;
height:2em;
transform-style: preserve-3d;
animation:a06043_a2 2s 0s linear infinite;
transform:
translate3d(0px,0px,-3em)
rotatey(-0deg);
}
@keyframes a06043_a2
{
0%
{
transform:
translate3d(0px,0px,-3em)
rotatey(0deg);
}
50%
{
transform:
translate3d(0px,0px,-8em)
rotatey(180deg);
}
100%
{
transform:
translate3d(0px,0px,-3em)
rotatey(360deg);
}
}
#a06043 .aa
{
position:absolute;
transform-style: preserve-3d;
width:0em;
height:0em;
border-width:0em 1em 2em 1em;
border-style:solid;
border-color:transparent transparent #f00 transparent;
opacity:0.9;
transform:
translate3d(0,0em,1em)
rotatex(45deg)
translate3d(0,-1em,0em);
}
#a06043 .a1
{
border-color:transparent transparent #ff8888 transparent;
transform:
rotatey(0deg)
translate3d(0,0em,1em)
rotatex(30deg)
translate3d(0,-1em,0em);
}
#a06043 .a2
{
border-color:transparent transparent #ffff88 transparent;
transform:
rotatey(90deg)
translate3d(0,0em,1em)
rotatex(30deg)
translate3d(0,-1em,0em);
}
#a06043 .a3
{
border-color:transparent transparent #8888ff transparent;
transform:
rotatey(180deg)
translate3d(0,0em,1em)
rotatex(30deg)
translate3d(0,-1em,0em);
}
#a06043 .a4
{
border-color:transparent transparent #88ff88 transparent;
transform:
rotatey(-90deg)
translate3d(0,0em,1em)
rotatex(30deg)
translate3d(0,-1em,0em);
}
#a06043 .a5
{
border-color:transparent transparent #ff88ff transparent;
transform:
scaley(-1)
rotatey(0deg)
translate3d(0,0em,1em)
rotatex(30deg)
translate3d(0,-1em,0em);
}
#a06043 .a6
{
border-color:transparent transparent #ffaa88 transparent;
transform:
scaley(-1)
rotatey(90deg)
translate3d(0,0em,1em)
rotatex(30deg)
translate3d(0,-1em,0em);
}
#a06043 .a7
{
border-color:transparent transparent #88ffff transparent;
transform:
scaley(-1)
rotatey(180deg)
translate3d(0,0em,1em)
rotatex(30deg)
translate3d(0,-1em,0em);
}
#a06043 .a8
{
border-color:transparent transparent #00ff88 transparent;
transform:
scaley(-1)
rotatey(-90deg)
translate3d(0,0em,1em)
rotatex(30deg)
translate3d(0,-1em,0em);
}
</style>
<div id="a06043">
<div class="view">
<div class="camera">
<div class="aa a1"></div>
<div class="aa a2"></div>
<div class="aa a3"></div>
<div class="aa a4"></div>
<div class="aa a5"></div>
<div class="aa a6"></div>
<div class="aa a7"></div>
<div class="aa a8"></div>
</div>
</div>
</div>
掌握這個方式後,就可以開始玩 3D 了,這裡寫了一個有趣的展示,只用 CSS 就可以做出早期德軍總部的隧道畫面,這裡用了一個參數 backface-visibility: hidden;-webkit-backface-visibility: hidden; 可以把背對的面隱藏,在這個範例中我有偷懶一下,並沒有把所有的面都設到正確的方向,只有一個 aa2 那個面需要在背對時隱藏。
<style>
#a06041 .window
{
position:relative;
width:300px;
height:300px;
overflow:hidden;
border:1px solid;
background-color:#aaffff;
}
#a06041 .view
{
position:absolute;
perspective:400px;
width:300px;
height:300px;
perspective-origin:50% 50%;
}
#a06041 .camera
{
position:absolute;
width:300px;
height:300px;
transform-style: preserve-3d;
animation:a06041_a1 5s 0s linear infinite;
transform:
translate3d(0px,0px,calc(150px))
rotate3d(0,1,0,90deg)
translate3d(0px,0px,calc(150px + 600px));
}
@keyframes a06041_a1
{
0%
{
transform:
translate3d(0px,0px,0)
rotate3d(0,1,0,0deg)
translate3d(0px,0px,0);
}
20%
{
transform:
translate3d(0px,0px,0)
rotate3d(0,1,0,20deg)
translate3d(0px,0px,100px);
}
40%
{
transform:
translate3d(0px,0px,0)
rotate3d(0,1,0,-20deg)
translate3d(0px,0px,200px);
}
70%
{
transform:
translate3d(0px,0px,calc(150px))
rotate3d(0,1,0,20deg)
translate3d(0px,0px,calc( 600px));
}
90%
{
transform:
translate3d(0px,0px,calc(150px))
rotate3d(0,1,0,80deg)
translate3d(0px,0px,calc(100px + 600px));
}
100%
{
transform:
translate3d(0px,0px,calc(150px))
rotate3d(0,1,0,90deg)
translate3d(0px,0px,calc(150px + 600px));
}
}
#a06041 .aa
{
position:absolute;
width: 300px;
height:300px;
background-image:url(https://yiharng.github.io/bird.jpg);
background-size:300px 300px;
transform-origin:150px 150px;
}
#a06041 .aa1
{
width: 600px;
transform:
rotate3D(0,1,0,90deg)
translate3d(150px,0px,150px);
}
#a06041 .aa2
{
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
width: 900px;
transform:
rotate3D(0,1,0,90deg)
translate3d(150px,0px,-150px)
;
}
#a06041 .aa3
{
height:900px;
transform:
rotate3D(1,0,0,-90deg)
translate3d(0px,150px,-150px)
;
}
#a06041 .aa4
{
height:900px;
transform:
rotate3D(1,0,0,-90deg)
translate3d(0px,150px,150px)
;
}
#a06041 .obj
{
position:absolute;
width:300px;
height:300px;
transform-style: preserve-3d;
}
#a06041 .obj1
{
position:absolute;
width:300px;
height:300px;
transform-style: preserve-3d;
transform:
translate3d(150px,00px,calc( -900px + 150px) )
rotate3D(0,1,0,-90deg)
;
}
</style>
<div id="a06041">
<div class="window">
<div class="view">
<div class="camera">
<div class="obj">
<div class="aa aa1"></div>
<div class="aa aa2"></div>
<div class="aa aa3"></div>
<div class="aa aa4"></div>
</div>
<div class="obj1">
<div class="aa aa1"></div>
<div class="aa aa2"></div>
<div class="aa aa3"></div>
<div class="aa aa4"></div>
</div>
</div>
</div>
</div>
</div>
加上 javascript 產生面, 可以做個球, 只要花點時間,光靠 CSS 就可以做出旋轉地球了呢 !
<style>
#a06042 .view
{
position:relative;
perspective:400px;
width:300px;
height:300px;
perspective-origin:50% 50%;
transform:scale(1);
}
#a06042 .camera
{
position:absolute;
width:300px;
height:300px;
transform-style: preserve-3d;
transform:
translate3d(0px,0px,0px)
rotate3d(0,1,0,0deg);
}
#a06042 .obj
{
position:absolute;
width:300px;
height:300px;
transform-style: preserve-3d;
animation:a06042_a2 10s 0s linear infinite;
transform:
translate3d(0px,0px,-400px)
rotate3d(1,0,0,0deg)
rotate3d(0,1,0,0deg);
}
@keyframes a06042_a2
{
0%
{
transform:
translate3d(0px,0px,-400px)
rotatex(0deg)
rotatey(0deg);
}
100%
{
transform:
translate3d(0px,0px,-400px)
rotatex(360deg)
rotatey(720deg);
}
}
#a06042 .bb
{
position:absolute;
width:60px;
height:60px;
left: calc(150px - 30px);
top: calc(150px - 30px);
background-color:#faa;
border:2px solid #000;
opacity:1;
transform:
rotate3D(0,1,0,50deg)
rotate3D(1,0,0,30deg)
translate3d(0px,0px,-250px);
}
</style>
<div id="a06042">
<div class="view">
<div class="camera">
<div class="obj">
</div>
</div>
</div>
</div>
<script>
(()=>
{
let i,j;
let b;
for (j=-4;j<5;j++)
for (i=0;i<12;i++)
{
b=$("<div class='bb'></div>");
b.css("transform",
"rotate3D(0,1,0,"+(i*30)+"deg) "
+"rotate3D(1,0,0,"+(j*20)+"deg) "
+"translate3d(0px,0px,250px)");
$("#a06042 .obj").append(b);
}
})();
</script>
因為 div 是矩形, 雖然可以利用 clip-path 來做裁切,但是裡頭的貼圖並不會跟著變形,如果把解析度提高,那麼每個面都會接近矩形,效果很不錯,只是效能會很差,在調整了一下適當大小,又用了些偷懶的方式,沒有精算的很準確, 最後附上旋轉五色鳥做為這篇的結束,
<style>
#a06045 .view
{
position:relative;
perspective:400px;
width:300px;
height:300px;
perspective-origin:50% 50%;
transform:scale(1);
}
#a06045 .camera
{
position:absolute;
width:300px;
height:300px;
transform-style: preserve-3d;
transform:
translate3d(0px,0px,0px)
rotate3d(0,1,0,0deg);
}
#a06045 .obj
{
position:absolute;
width:300px;
height:300px;
transform-style: preserve-3d;
animation:a06045_a2 10s 0s linear infinite;
transform:
translate3d(0px,0px,-400px)
rotate3d(1,0,0,0deg)
rotate3d(0,1,0,0deg);
}
@keyframes a06045_a2
{
0%
{
transform:
translate3d(0px,0px,-400px)
rotatey(0deg);
}
100%
{
transform:
translate3d(0px,0px,-400px)
rotatey(360deg);
}
}
#a06045 .bb
{
position:absolute;
width:60px;
height:50px;
left: calc(150px - 30px);
top: calc(150px - 25px);
background-color:#faa;
border:0px solid #000;
opacity:1;
transform:
rotate3D(0,1,0,50deg)
rotate3D(1,0,0,30deg)
translate3d(0px,0px,-250px);
}
</style>
<div id="a06045">
<div class="view">
<div class="camera">
<div class="obj">
</div>
</div>
</div>
</div>
<script>
(()=>
{
let i,j;
let b;
let xx,yy;
let degy;
degy=11;
xx=16;
for (j=-5;j<6;j++)
for (i=0;i<xx;i++)
{
k=-j;
b=$("<div class='bb'></div>");
let r=250*Math.cos(k*degy*3.14/180)*2*3.14/xx*1.06;
b.css("width",r);
b.css("left","calc(150px - "+(r/2)+"px)");
b.css("background-image","url(https://yiharng.github.io/bird.jpg)");
let sizey=r*0.4*xx;
b.css("background-size",(r*xx)+"px "+sizey+"px ");
b.css("background-position"
,(r*(xx-i))+"px "+(sizey*(6-j)/13+50)+"px");
b.css("transform",
"rotate3D(0,1,0,"+(i*(360/xx))+"deg) "
+"rotate3D(1,0,0,"+(k*degy)+"deg) "
+"translate3d(0px,0px,250px)");
$("#a06045 .obj").append(b);
}
})();
</script>
沒有留言:
張貼留言