css3D的魅力

子慕大詩人發表於2019-03-02

前言:

最近玩了玩用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大部分時候使用場景不多,同時也比較消耗裝置效能,如果有機會用到,能在網頁中給使用者體驗帶來那麼一點點驚喜,也是不錯的。

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

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

相關文章