接受前端挑戰:CSS 3D Cube

已發表: 2022-03-10
快速總結↬你喜歡挑戰嗎? 你願意承擔你以前從未遇到過的任務,並在截止日期前完成嗎? 如果在執行任務的過程中遇到一個似乎無法解決的問題怎麼辦? 我想分享我在實際項目中第一次使用 CSS 3D 效果的經驗,並激勵您接受挑戰。 這是一個普通的日子, CreativePeople的經理 Eugene 給我寫信。 他給我發了一段視頻,並解釋說他正在為一個新項目開發一個概念,並想知道我是否有可能開發出類似於視頻中的東西。

你喜歡挑戰嗎? 你願意承擔你以前從未遇到過的任務,並在截止日期前完成嗎? 如果在執行任務的過程中遇到一個似乎無法解決的問題怎麼辦? 我想分享我在實際項目中第一次使用 CSS 3D 效果的經驗,並激勵您接受挑戰。

這是一個普通的日子,CreativePeople 的經理 Eugene 給我寫信。 他給我發了一段視頻,並解釋說他正在為一個新項目開發一個概念,並想知道我是否有可能開發出類似於視頻中的東西。

關於 SmashingMag 的進一步閱讀

  • Beercamp:CSS 3D 實驗
  • 使用 Clip-Path 創建響應式形狀並打破常規
  • 讓我們玩一下硬件加速的 CSS

它是一個圍繞其中一個軸旋轉的 3D 對象(準確地說是長方體)。 我已經有了一些使用 CSS 3D 的經驗,並且開始在我的腦海中形成一個解決方案。 我在 Google 上搜索了“CSS 3D 立方體”之類的關鍵字來確認我的想法,並回答 Eugene 說這是可能的。

跳躍後更多! 繼續往下看↓

尤金的下一個問題是我是否願意接手這個項目? 我喜歡棘手的任務,所以我無法拒絕。 當時,我並沒有意識到自己在做什麼,但我已經下定了決心。

磨礪你的斧頭

讓我們提醒自己關於軸——不是戰爭軸,而是數軸,與我們在學校學習的三維笛卡爾坐標系中的軸相同。 正如維基百科告訴我們的:

三維空間的笛卡爾坐標係是有序的三重線(軸),它們成對垂直,所有三個軸都有一個長度單位,每個軸都有一個方向。

下圖顯示了軸在 Web 瀏覽器中的方向。

Z 軸指向觀察者的右手三維笛卡爾坐標系。
右手三維笛卡爾坐標系,z 軸指向觀察者。 (圖片:維基共享資源)(查看大圖)

x 軸是水平的,y 軸是垂直的,z 軸看起來像是從屏幕上朝你走來的。 z 軸的零值是屏幕的平面。 記住這一點。

清理視角

要創建一個 3D 對象,我需要一個帶有透視圖的元素(我們稱之為“場景”)。 透視是場景的深度,它取決於它包含的對象的大小。

 .scene { perspective: 800px; }

如果視角太小,物體可能會變形。 如果太大,則 3D 效果將降低到零。

在 CodePen 上查看 Anna Selezniova (@askd) 的 Pen jqgMvL。

此外,場景中的所有對像只有一個視角。 3D 效果取決於視點的位置。

在 CodePen 上查看 Anna Selezniova (@askd) 的 Pen oxKzKv。

那麼,我們如何計算透視呢? 我發現它取決於旋轉軸。 對於 x 軸,高度值乘以 4 即可。 對於 y 軸,它將是寬度值乘以 4。這是我的神奇公式:

 const perspective = dimension * 4;

全方位考慮

確定透視後,我開始創建一個 3D 對象。 我選擇了一個立方體,因為它簡單且可預測。 一個立方體元素被創建為一個相對定位的常規 div,並定義了寬度和高度(例如200px )。 它通過值為preserve-3dtransform-style屬性轉換為 3D 對象。 它告訴瀏覽器根據 3D 世界的規則渲染所有嵌套元素。

在我的例子中,立方體有六個絕對定位的 div(或“邊”)。 類名對應於邊的初始位置( back , left , right , top , bottom , front )。 這是標記:

 <div class="scene"> <div class="cube"> <div class="side back"></div> <div class="side left"></div> <div class="side right"></div> <div class="side top"></div> <div class="side bottom"></div> <div class="side front"></div> </div> </div>

默認情況下,所有邊都在一個平面上。 所以,我需要重新排列它們。 看起來是這樣的:

在 CodePen 上查看 Anna Selezniova (@askd) 的 Pen mPNwPx。

這是生成的 CSS:

 .cube { position:relative; width: 200px; height: 200px; transform-style: preserve-3d; } .side { position: absolute; width: 200px; height: 200px; } .back { transform: translateZ(-100px); } .left { transform: translateX(-100px) rotateY(90deg); } .right { transform: translateX(100px) rotateY(90deg); } .top { transform: translateY(-100px) rotateX(90deg); } .bottom { transform: translateY(100px) rotateX(90deg); } .front { transform: translateZ(100px); }

為了旋轉立方體,我將立方體元素的transform屬性設置為沿 x 軸的任意旋轉角度:

 .cube { transform: rotateX(42deg); }

克服缺點

根據作業,我只是沿x軸旋轉立方體,所以我不需要左側或右側。 我添加了標題以與剩餘邊的初始位置對齊。

我開始旋轉立方體,發現底部和背面的字幕顯示是倒置的:

請參閱 CodePen 上 Anna Selezniova (@askd) 的 Pen GZVvMR。

為了解決這個問題,我將這些邊沿 x 軸旋轉了 180 度:

 .back { transform: translateZ(-100px) rotateX(180deg); } .bottom { transform: translateY(100px) rotateX(270deg); }

超越屏幕

我開始用真實的內容填充兩邊,然後立即遇到了另一個問題。 我需要顯示 1 像素的虛線,但它們很模糊而且看起來很糟糕。

請參閱 CodePen 上 Anna Selezniova (@askd) 的 Pen VjeBPg。

我很快意識到問題出在哪裡。 你還記得那個圖像超出屏幕的 3D 電視廣告嗎? 我的立方體就是這樣。

如果您可以從左側或右側查看立方體,您會看到它的中心位於屏幕平面上(z 軸為零),並且正面超出屏幕。 因此,它在視覺上增加和模糊。

在 CodePen 上查看 Anna Selezniova (@askd) 的 Pen WwVEMR。

為了解決這個問題,我沿 z 軸移動了立方體以將正面與屏幕平面對齊:

 .cube { transform:translateZ(-100px); }

現在是立方體,幾乎準備好了:

請參閱 CodePen 上 Anna Selezniova (@askd) 的 Pen Xdvery。

使用幻數

我想你已經註意到我正在使用幻數100來沿軸移動邊。 值100正好是我的測試立方體高度的一半。 為什麼是一半的高度? 因為那將是內接在立方體一側的圓的半徑(顯然是正方形)。

 const offset = dimension / 2;

如果我需要旋轉一個三棱柱,這個圓將被內接在一個三角形中。 在這種情況下,偏移量的公式如下:

 const offset = dimension / (2 * Math.sqrt(3));

吹走立方體

為了完成任務,我不得不在不同的瀏覽器中測試結果。

我在 Internet Explorer 中看到的圖片讓我陷入了沮喪。 要了解我在說什麼,請在您喜歡的瀏覽器中查看下面的演示。 我更改了一個屬性,導致多維數據集在 Internet Explorer 中顯示不正確。 但是,在您閱讀下面演示下的段落之前,請不要偷看源代碼。

請參閱 CodePen 上 Anna Selezniova (@askd) 的 Pen XKWMwV。

事實上,Internet Explorer 不支持值為preserve-3dtransform-style屬性。 我通過查看我的可信賴資源 Can I Use(見註 1)了解了這一點。 在上面的演示中,我將preserve-3d替換為flat 。 你已經知道了嗎? 嘿,我告訴過你不要偷看!

我很沮喪,但我不打算放棄。 問題是學習新事物的機會。 此外,我已經接受了挑戰。

尋找支點

我一直在尋找一種不使用transform-style: preserve-3d創建 3D 對象的方法,最終我發現了一個有用的屬性: transform-origin 。 它決定了元素變換的中心點。 我在下面創建了一個交互式演示,它將幫助您了解它的工作原理:

在 CodePen 上查看 Anna Selezniova (@askd) 的 Pen rLNmBp。

演示中元素的 3D 旋轉非常類似於立方體的正面,不是嗎? 這就是我用的。

(順便問一下,您是否嘗試選擇複選框backface-visibility: hidden during the 3D 旋轉?此屬性用於在 3D 轉換期間隱藏元素的背面。)

重新開始

我開始重做立方體。 我不必與整個場景進行交互,因此我移除了scene元素的perspective屬性並將其添加到每個 3D 變換中,這樣現在每個元素都可以獨立變換。 此外,我為每一面設置了新屬性: transform-origin的值等於立方體中心的位置,以及backface-visibility: hidden 。 以下是樣式的變化:

 .scene { } .cube { position: relative; width: 200px; height: 200px; transform: perspective(800px) translateZ(-100px); } .side { position: absolute; transform-origin: 50% 50% -100px; backface-visibility: hidden; }

我必須把兩邊放在正確的地方。 由於transform-origin屬性,我不需要移動它們,只需要圍繞軸旋轉它們。 就像魔術一樣! 讓我們看看它的外觀:

請參閱 CodePen 上 Anna Selezniova (@askd) 的 Pen zBYwEm。

這是側面放置的CSS:

 .back { transform: perspective(800px) rotateY(180deg); } .top { transform: perspective(800px) rotateX(90deg); } .bottom { transform: perspective(800px) rotateX(-90deg); } .front { transform: perspective(800px); }

在這裡您可以看到正在運行的新立方體:

請參閱 CodePen 上 Anna Selezniova (@askd) 的 Pen wWvdXd。

還給凱撒什麼是凱撒的

第二個立方體的外觀和旋轉與第一個相同。 但在這種情況下,您需要單獨轉換每一面。 這可能不是很容易,特別是如果您想控制中間旋轉角度。

此外,如果您在 Chrome 中打開演示,您會看到旋轉過程中兩側會閃爍——非常令人沮喪。

最後,我使用transform-style: preserve-3d的簡單測試應用了這兩種方法。 第一個立方體是默認的。 第二個多維數據集用於 Internet Explorer 和不支持preserve-3d瀏覽器。

使用數學的力量

最後,我必須實現視差效果。 通常,此效果會響應用戶的操作,無論是鼠標光標的位置還是滾動條的位置。 在這種情況下,效果取決於旋轉角度。

請參閱 CodePen 上 Anna Selezniova (@askd) 的 Pen QENyqm。

那麼,我有什麼數據? 首先,我有字幕位置的起點和終點,或者簡單地說,就是它相對於邊中心的上下offset 。 其次,我有立方體的旋轉angle

我花了幾個小時試圖開發一個公式。 然後,我恍然大悟。 這就是我想到的:

正弦和余弦函數圖
正弦和余弦函數圖(圖片:維基共享資源)(查看大圖)

在正弦和余弦的幫助下,我很容易根據角度計算出每個字幕的偏移量。 以下是我想出的公式:

 const front_offset = offset * sin(angle) * -1; const bottom_offset = offset * cos(angle); const back_offset = offset * sin(angle); const top_offset = offset * cos(angle) * -1;

加起來

作業現已完成,我可以享受結果並與您分享。 親自看看它是如何工作的。 使用滾動或箭頭鍵來旋轉促銷塊。 另外,請嘗試上下拉動右側的黑色三角形以手動控制旋轉角度(不幸的是,此功能在 Internet Explorer 中不起作用)。 看起來很不錯,不是嗎? 並且性能相當高(大約每秒 60 幀)。

我很高興參與了這個網站的開發。 我在使用 CSS 3D 方面獲得了有用的經驗,並發現了許多有趣的屬性。 更重要的是,我知道一個人永遠不應該放棄; 您很可能會找到完成任務的方法。

我希望你喜歡我的故事,現在準備好迎接新的挑戰。