1. 程式人生 > >CSS 3D 的魅力

CSS 3D 的魅力

作者 | 子慕大詩人

來源 | www.cnblogs.com/1wen/p/9064011.html

 

前言:

 

最近玩了玩用css來構建3D效果,寫了幾個demo,所以部落格總結一下。  在閱讀這篇部落格之前,請先自行了解一下css 3D的屬性,例如:transform-style,transform-origin,transform, perspective。

 

demo1

 

高度可變的立方體,先來看看最終效果,自己弄得有點醜,如果設計師調下色,新增點元素應該會好看的多

 

 

 

1.  我們先用css實現一個長方體,一個長方體有6個邊,我們寫6個div,並用一個div包裹起來

 

<div class="cube-box">

    <div class="cube1 cube"></div>

    <div class="cube2 cube"></div>

    <div class="cube3 cube"></div>

    <div class="cube4 cube"></div>

    <div class="cube5 cube"></div>

    <div class="cube6 cube"></div>

</div>

 

2.  給.cube-box設定寬高以及preserve-3d屬性保留子元素3d轉換,子元素.cube全部絕對定位

 

.cube-box{

    transform-style: preserve-3d;

    width: 30px;

    height: 100px;

    position: relative;

}

.cube{

    position: absolute;

    left: 0;

    top: 0;

}

 

3.  先寫一個面.cube1,寬高100%等同於父元素的寬高,背景色為red,程式碼和效果如下

 

 

.cube1{

    width: 100%;

    height: 100%;

    background: red;

}

 

 

 

4.  為了之後方便我們看到立體效果,現在我們旋轉一下父元素,加入如下程式碼,效果如下

 

.cube-box{

    transform: rotateX(-30deg) rotateY(45deg);

}

 

 

 

5.  .cube1作為第一個元素,我們不需要它旋轉,它作為預設面,現在拼接第二個面.cube2,按照.cube1的寫法,但是我們設定為綠色,效果如下,.cube2重疊在.cube1上,因此我們還需要旋轉.cube2

 

.cube2{

    width: 100%;

    height: 100%;

    background: green;

}

 

 

 

6.  我們現在試著旋轉一下.cube2,變成了如下效果。關於rotate的旋轉方向這裡不解釋,不懂的朋友可以自行檢視其他文件。

 

.cube2{

    width: 100%;

    height: 100%;

    background: green;

    transform: rotateY(-90deg);

}

 

 

 

7.  在用translate3d移動一下吧。  效果如下圖,屌屌屌。  但是問題來了,這裡的程式碼不夠靈活,translate的值需要手動計算,現在寬是30px,需要移動它的一半15px進行拼接,這個值需要我們手動計算寫上去,或者到時候用js計算,太low,我希望只需要用js根據後端資料動態設定父元素.cube-box的寬高,子元素全部自適應就行,這樣才更好用。

 

.cube2{

    width: 100%;

    height: 100%;

    background: green;

    transform: rotateY(-90deg) translate3d(15px,0,15px);

}

 

 

 

8.  因此現在我們要使用另一個屬性transform-origin,transform-origin預設是“center center 0;”或者說“50% 50% 0;”,所以在第6個步驟的時候,我們旋轉.cube2的時候是根據它自身中間的位置進行的旋轉,我們改造一下,把轉換的位置定在元素左邊,也同樣達到了效果,程式碼反而更簡單了

 

.cube2{

    width: 100%;

    height: 100%;

    background: green;

    transform-origin: left top;

    transform: rotateY(-90deg);

}

 

 

 

9.  按照.cube2的方法我們給.cube3按照同樣的寫法旋轉,並設定藍色,效果如下

 

.cube3{

    width: 100%;

    height: 100%;

    background: blue;

    transform-origin: right top;

    transform: rotateY(90deg);

}

 

 

 

10.  .cube4就有點不一樣了,下一個面不需要旋轉,只需要把.cube1向Z軸方向移動30px寬的位置,X和Y軸可以用width和height作為基數設定百分比,比如width是20px,如果要X軸移動20px,可以設定translateX(100%),但是Z就只能用具體值了。  所以這裡我沒有解決low的問題,我只能手動的寫上translateZ的值,或者用js來動態賦值。  效果如下,如果有更好的方案,可以評論部落格告知我。

 

.cube4{

    width: 100%;

    height: 100%;

    background: gray;

    transform-origin: right top;

    transform: translateZ(30px);

}

 

 

 

11.  .cube5也就是頂面,我們的頂面和低面都是正方形的,.cube5如果寫寬高100%就是長方形了,為了不手動或者動態寫高度,這裡使用了另一種寫法設定width:100%;不設定height,設定padding-top:100%;這樣同樣使.cube5變成了正方形,定義粉紅色,延X軸旋轉90度,程式碼和效果如下

 

.cube5{

    width: 100%;

    padding-top: 100%;

    background: pink;

    transform-origin: left top;

    transform: rotateX(90deg);

}

 

 

 

12.   最後.cube6和.cube5寫法一樣,只是我們需要把位置絕對定位到底部,這時候把.cube類設定為透明度50%,方便我們檢視,程式碼和效果如下

 

.cube6{

    width: 100%;

    padding-top: 100%;

    background: black;

    top: inherit;

    bottom: 0;

    transform-origin: left bottom;

    transform: rotateX(-90deg);

}

 

 

 

13.  我們把每一個面都定義為紅色,調整一下每一個面的顏色值,這樣看起來就有視角的效果

 

 

 

14.   現在長方體已經寫好,我們來點動效吧,新增一個div.cube-wapper把剛才的cube-box再包裹一層,讓cube-box絕對定位到父元素底部,這樣高度變化的時候是向上延伸和收縮,js定時器每隔5秒改變一下box的高度,效果如下

 

let boxs = document.getElementsByClassName('cube-box');

setInterval(()=>{

    for(let item in boxs){

        if(boxs[item].style) boxs[item].style.height = `${Math.random()*300}px`;

    }

},5000)

 

 

 

15.  不對啊,怎麼底部還是有移動?  原因是我們tranform的rotate寫在了.cube-box上,當高度改變的時候,會受到旋轉的影響導致位置偏移,因此把.cube-box的tranform寫到.cube-wrapper上去便沒有這個問題了。效果如下

 

 

 

 

demo2

 

一個圓柱體,因為被轉換為gif效果有點差,實際執行會好很多。  這個的實現比較奇葩,在實際場合中幾乎沒有什麼卵用,下面我還是大致說下實現方法吧。

 

 

 

1.  還是和demo1差不多,先定義包裹層,定義preserve-3d,程式碼大致如下

 

<div class="wrapper">

    <div class="box" id="circles">

    </div>

</div>

 

<style>

.wrapper{

    transform-style: preserve-3d;

    width: 100px;

    height: 100px;

}

.box{

    width: 100%;

    height: 100%;

    position: relative;

    transform-style: preserve-3d;

    transform: rotateX(-73.5deg) rotateY(5deg);

}

</style>

 

2.  在box裡插入n個div,每一個<div樣式相同設定為border-radius:50%和1px的border邊框,唯一不同的是它們的translateZ位置相鄰相差1,其實就是把1px的邊框依次排列起來形成一個圓柱,這樣會需要生成很多div,最後我們還是用js去生成我們需要的div數量。

 

let circles = document.getElementById('circles');

for(let i=0;i<100;i++){

    let div = document.createElement('div');

    div.style = `transform: translateZ(${i}px)`;

    div.className = `circle ${i==0||i==99?'bg':''}`;

    circles.appendChild(div);

}

 

n個1px的div是無縫拼接起來的,為什麼還是會有縫隙呢?  大家想象一張紙畫了一個圈,從紙的最薄的一面看,是不是看不到圈了,如果再轉換一點角度,也許也只能看到一點點,就是這個道理。如此方式我還試了下,寫一個球,這裡不傳gif了,截圖看看效果,github上會有程式碼可以親自下載下來看看,效果還是挺神奇的

 

 

 

 

 

 

 

demo3

 

串掛的卡片效果,效果大致如下,像是掛在線上的6張照片,還帶一點風吹的效果。  實際也非常簡單,還是利用上面demo1的原理旋轉卡片,再通過定位把卡片排列,定義一個無限迴圈的搖擺動畫,給每個卡片使用不同的時間,最後繫結點選事件,給元素使用css過渡動畫transition。  過渡動畫保證元素改變或者還原的時候,都能有效果,所以過渡動畫很適合用來做互動。  注意: 進行了3d轉換後,要注意元素的可點選區域,用chrome除錯工具檢視比較準確。

 

 

 

結語:

 

css 3d大部分時候使用場景不多,同時也比較消耗裝置效能,如果有機會用到,能在網頁中給使用者體驗帶來那麼一點點驚喜,也是不錯的。

 

好了,我知道大家需要什麼,倉庫地址已經準備好 https://github.com/zimv/css3d。

 

都看到這裡了,要不點個zan~