CSS 繪製一個3d掘金 logo

XboxYan 發表於 2022-11-22
CSS

嘗試一下用 CSS 繪製簡單的 3d 圖形,比如一個掘金 logo?

Kapture 2022-11-12 at 13.58.00

相比 2d 繪製,3d 有哪些需要注意的小細節呢?一起看看吧

一、金字塔形/四稜錐形

除去挖空部分,整個外形其實是一個金字塔形,或者叫四稜錐形(四角錐)

image-20221112140904640

一共有5個面,所以我們可以準備5個元素

<juejin>
  <pane a></pane>
  <pane b></pane>
  <pane c></pane>
  <pane d></pane>
  <bottom></bottom>
</juejin>

其中,bottom表示底部的正方形,其餘4個表示側面4個三角形。

首先先從正方體開始

注意,需要呈現 3d 視覺,需要新增transform-style: preserve-3d,先繪製一個側面

juejin{
  --s: 200px;
  position: relative;
  width: var(--s);
  height: var(--s);
  transform-style: preserve-3d;
  perspective: 3000px;
  transform: rotateX(calc( .3 * -90deg)) rotateY(calc( .1 * 90deg));
}
pane{
  position: absolute;
  inset: 13.3% 0 0;
  transform-origin: center bottom;
}
pane[a]{
  background-color: #368dff;
  transform: translate3d(0, 0, calc(var(--s) * -0.5));
}

效果如下

image-20221112141726969

用同樣的方式,繪製出4個側面

pane[b]{
  background-color: #368dff;
  transform: translate3d(0, 0, calc(var(--s) * 0.5));
}
pane[c]{
  background-color: #1e80ff;
  transform: translate3d(calc(var(--s) * -0.5), 0, 0) rotateY(90deg);
}
pane[d]{
  background-color: #1e80ff;
  transform: translate3d(calc(var(--s) * 0.5), 0, 0) rotateY(90deg);
}

效果如下

image-20221112142105423

然後,將4個側面裁剪成三角形,用clip-path就行了

pane{
  /**/
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}

效果如下

image-20221112142330741

最後,將4個側面沿著底部旋轉一個角度,使其合併在一起,由於大小完全一樣,底部又是正方形,傾斜角度自然也相同,這裡就不透過數學計算了,直接用一個 CSS 變數去實時比對

juejin{
  /**/
   --deg: 35.3deg;
}
pane[a]{
  /**/
  transform: translate3d(0, 0, calc(var(--s) * -0.5)) rotateX(calc(var(--deg) * -1));
}
pane[b]{
  /**/
  transform: translate3d(0, 0, calc(var(--s) * 0.5)) rotateX(var(--deg));
}
pane[c]{
  /**/
  transform: translate3d(calc(var(--s) * -0.5), 0, 0) rotateY(90deg) rotateX(calc(var(--deg) * -1));
}
pane[d]{
  /**/
  transform: translate3d(calc(var(--s) * 0.5), 0, 0) rotateY(90deg) rotateX(var(--deg));
}

效果如下

Kapture 2022-11-12 at 14.29.52

這樣就得到了一個金字塔形/四稜錐形

image-20221112143248362

二、側邊鏤空和橫截面處理

前面得到了一個完整的四稜錐,現在需要先將側面鏤空。

這裡有兩種方式,第一,可以用透明和不透明的漸變填充,這樣透明的部分就是鏤空的了;還有一種方式,用mask遮罩,也能輕易的實現

pane{
  /*  */
  -webkit-mask: linear-gradient(to bottom, red 50%, transparent 0) 0 0/ 100% 40%;
}

效果如下

Kapture 2022-11-12 at 14.41.20

但是現在還是看著像紙片一樣,需要將截斷的幾個面都封起來,我們再加一個標籤,用兩個偽元素將頂部覆蓋

<juejin>
  <!-- -->
  <top></top>
</juejin>
top{
  position: absolute;
  inset: 0;
  background-color: #1677f7;
  transform: translate3d(0, calc(var(--s) * 0.36), 0) rotateX(90deg) scale(.8);
  transform-style: preserve-3d;
  box-shadow: inset 0 0 calc(var(--s) * 0.3) rgb(255 255 255 / 45%);
}
top::before{
  content: '';
  position: absolute;
  inset: 0;
  background-color: inherit;
  transform: translate3d(0, 0, calc(var(--s) * 0.285)) scale(.5);
  box-shadow: inherit;
}

當然,這裡需要多多調整一下細節,確保橫截面的大小和位置剛好吻合

Kapture 2022-11-12 at 14.47.11

同樣還有底部的截面

bottom::before,
bottom::after{
  content: '';
  position: absolute;
  inset: 0;
  background-color: inherit;
  transform: translate3d(0, 0, calc(var(--s) * 0.285)) scale(.595);
  box-shadow: inherit;
}
bottom::after{
  transform: translate3d(0, 0, calc(var(--s) * 0.568)) scale(.195)
}

效果如下

Kapture 2022-11-12 at 14.50.11

三、其他光影細節和滑鼠跟隨效果

首先是底部的投影,這是透過filter: blur實現的,可以讓整個場景更具空間感

juejin::after{
  position: absolute;
  content: '';
  width: var(--s);
  height: var(--s);
  top: 80%;
  background-color: rgba(0, 0, 0, 0.1);
  transform: rotateX(90deg) scale(1.2);
  filter: blur(20px);
}

Kapture 2022-11-12 at 14.54.07

然後是截面的高光處理,這裡是透過內陰影實現的,讓整體光影看著更加自然,就像上方有光線一樣

top{
  /**/
  box-shadow: inset 0 0 calc(var(--s) * 0.3) rgb(255 255 255 / 45%);
}

Kapture 2022-11-12 at 14.57.20

最後是滑鼠跟隨效果,這裡是透過 CSS 變數實現的,透過 JS 獲取滑鼠的相對位置,然後透過 CSS 變數 傳遞,實時改變 transform 的旋轉角度

juejin{
  /* */
  transform: rotateX(calc( (var(--y) - 0.3) * -90deg)) rotateY(calc( (var(--x) - 0.3) * 90deg));
}

JS 監聽mousemove就可以了

document.body.addEventListener('mousemove', function(ev){
  document.body.style.setProperty('--x', ev.clientX / document.body.offsetWidth)
  document.body.style.setProperty('--y', ev.clientY / document.body.offsetHeight)
})

這樣就得到了文章開頭所示效果

Kapture 2022-11-12 at 13.58.00

完整程式碼也可以檢視以下任意連結:

四、總結一下

總的來說,繪製這樣一個的 3d 圖形還是比較輕鬆的,下面總結一下

  1. 新增transform-style: preserve-3d才能呈現 3d 視覺
  2. 掘金 logo 整體上是一個金字塔形,或者叫四稜錐形(四角錐)
  3. 整個繪製就是透過translate3drotate3d等基礎操作組合而成
  4. 鏤空部分可以透過漸變填充,或者mask遮罩實現
  5. 橫截面需要多多調整一下細節,確保橫截面的大小和位置剛好吻合
  6. 為了讓整個模型更加真實,需要新增合理的高光和投影
  7. 滑鼠跟隨效果可以透過 CSS 變數傳遞實現

當然,像這種比較規則的圖形還能簡單透過CSS應付一下,如果有一些不規則的,或者有曲面,那就不太好實現了,而且高光也無法真正模擬出來,像這種情況還是建議透過three.js這樣專業的圖形處理庫才行。最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤