手把手教你用CSS实现一个VR 3D游戏菜单栏效果
3D游戲菜單組件
關于如何建立一個響應性、適應性和無障礙的3D游戲菜單的基礎性概述。
在這篇文章中,我想帶著大家寫一個3D游戲菜單組件的案例。首先讓我們看看成品是什么樣子的
概述
相信大家都玩過賽博朋克吧,最近正好迷上了這個游戲,里面的菜單欄深深引起了我的注意,特別是在新的AR/VR游戲中,讓菜單看起來漂浮在空間中也是很炫的事情。今天我們講重現這種效果的精髓,但要加上自適應和色彩方案。
我們會使用實驗性的CSS @custom-media和@nest來防止重復的媒體查詢,并將媒體查詢集中在組件樣式塊中。這些規范中提出的語法可以通過PostCSS和這兩個插件實現:postcss-custom-media和postcss-nesting。
HTML
游戲菜單是一個按鈕的列表。用HTML表示的最佳方法如下。
<ul class="threeD-button-set"><li><button>New Game</button></li><li><button>Continue</button></li><li><button>Online</button></li><li><button>Settings</button></li><li><button>Quit</button></li> </ul>效果:
CSS
設計按鈕列表的樣式可分為以下幾個高級步驟。
自定義屬性概述
自定義屬性通過給其他看起來很隨機的值起有意義的名字來幫助區分這些值,避免重復的代碼和在子項之間共享值。
下面是保存為CSS變量的媒體查詢,也被稱為自定義媒體。這些是全局性的,將在各種選擇器中使用,以保持代碼的簡潔和可讀性。游戲菜單組件使用運動偏好、系統色彩方案和顯示器的色彩范圍能力。
@custom-media --motionOK (prefers-reduced-motion: no-preference); @custom-media --dark (prefers-color-scheme: dark); @custom-media --HDcolor (dynamic-range: high);下面的自定義屬性管理顏色方案并保持鼠標位置值,以使游戲菜單在懸停時具有交互性。命名自定義屬性有助于代碼的可讀性,因為它揭示了該值的用例或該值的結果的友好名稱。
.threeD-button-set {--y:;--x:;--distance: 1px;--theme: hsl(180 100% 50%);--theme-bg: hsl(180 100% 50% / 25%);--theme-bg-hover: hsl(180 100% 50% / 40%);--theme-text: white;--theme-shadow: hsl(180 100% 10% / 25%);--_max-rotateY: 10deg;--_max-rotateX: 15deg;--_btn-bg: var(--theme-bg);--_btn-bg-hover: var(--theme-bg-hover);--_btn-text: var(--theme-text);--_btn-text-shadow: var(--theme-shadow);--_bounce-ease: cubic-bezier(.5, 1.75, .75, 1.25);@media (--dark) {--theme: hsl(255 53% 50%);--theme-bg: hsl(255 53% 71% / 25%);--theme-bg-hover: hsl(255 53% 50% / 40%);--theme-shadow: hsl(255 53% 10% / 25%);}@media (--HDcolor) {@supports (color: color(display-p3 0 0 0)) {--theme: color(display-p3 .4 0 .9);}} }淺色和深色主題圓錐形背景
淺色主題有一個鮮艷的青色到粉紅色的圓錐漸變,而深色主題有一個暗色的微妙圓錐漸變。
html {background: conic-gradient(at -10% 50%, deeppink, cyan);@media (--dark) {background: conic-gradient(at -10% 50%, #212529, 50%, #495057, #212529);} }啟用3D透視功能
為了讓元素存在于網頁的三維空間中,需要初始化一個帶透視的視口。我選擇把透視放在主體元素上,并使用視口單元來創建我喜歡的風格。
body {perspective: 40vw; }這就是視角所能產生的影響。
為<ul>按鈕列表設計樣式
這個元素負責整個按鈕列表的宏觀布局,同時也是一個互動的、3D的浮動卡片。這里有一個實現的方法。
鈕扣組布局
Flexbox可以管理容器的布局。通過flex-direction將flex的默認方向從行改為列,并通過align-items從stretch改為start來確保每個項目都是其內容的大小。
.threeD-button-set {/* remove <ul> margins */margin: 0;/* vertical rag-right layout */display: flex;flex-direction: column;align-items: flex-start;gap: 2.5vh; }接下來,將容器建立為一個三維空間背景,并設置CSS clamp()函數,以確保卡片的旋轉不會超過可識別的旋轉。注意,夾子的中間值是一個自定義屬性,這些--x和--y值將在以后的鼠標交互時從JavaScript中設置。
.threeD-button-set {…transform-style: preserve-3d;transform:rotateY(clamp(calc(var(--_max-rotateY) * -1),var(--y),var(--_max-rotateY)))rotateX(clamp(calc(var(--_max-rotateX) * -1),var(--x),var(--_max-rotateX))); }接下來,如果運動對訪問用戶來說是可以的,就向瀏覽器添加一個提示,說明這個項目的變換會隨著will-change不斷變化。此外,通過在變換上設置一個過渡來實現插值。這個過渡將在鼠標與卡片互動時發生,使旋轉變化的過渡平穩。該動畫是一個持續運行的動畫,即使鼠標不能或沒有與該組件互動,也能展示出該卡所處的三維空間。
@media (--motionOK) {.threeD-button-set {will-change: transform;transition: transform .1s ease;animation: rotate-y 5s ease-in-out infinite;} }旋轉-y動畫只將中間的關鍵幀設置為50%,因為瀏覽器會將0%和100%默認為該元素的默認樣式。這是對交替出現的動畫的簡稱,需要在同一位置開始和結束。這是一種闡述無限交替動畫的好方法。
@keyframes rotate-y {50% {transform: rotateY(15deg) rotateX(-6deg);} }為<li>元素定型
每個列表項(<li>)都包含按鈕和它的邊框元素。顯示樣式被改變了,所以該項目不顯示::標記。位置樣式被設置為相對的,所以即將到來的按鈕偽元素可以將自己定位在按鈕所消耗的全部區域內。
.threeD-button-set > li {display: inline-flex;position: relative;transform-style: preserve-3d; }Styling the <button>elements
為按鈕設計造型是一項艱難的工作,有很多狀態和交互類型需要考慮。由于要平衡偽元素、動畫和交互,這些按鈕很快就會變得復雜。
初始<button>樣式
以下是將支持其他狀態的基礎風格。
.threeD-button-set button {appearance: none;outline: none;border: none;background-color: var(--_btn-bg);color: var(--_btn-text);text-shadow: 0 1px 1px var(--_btn-text-shadow);font-size: 5vmin;font-family: Audiowide;padding-block: .75ch;padding-inline: 2ch;border-radius: 5px 20px; }按鈕的偽元素
按鈕的邊框不是傳統的邊框,它們是有邊框的絕對位置的偽元素。
這些元素對于展示已經建立的3D視角至關重要。這些偽元素中的一個會被推離按鈕,另一個會被拉近到用戶身邊。這種效果在頂部和底部的按鈕中最為明顯。
3D transform styles
下面的transform-style被設置為preserve-3d,這樣就可以在Z軸上拉開空間。變換被設置為–距離的自定義屬性,它將在懸停和聚焦時被增加。
.threeD-button-set button {…transform: translateZ(var(--distance));transform-style: preserve-3d;&::after {/* 以3倍的倍數在Z空間向前拉動 */transform: translateZ(calc(var(--distance) / 3));}&::before {/* 在Z空間以3倍的倍數推回 */transform: translateZ(calc(var(--distance) / 3 * -1));} }條件性動畫樣式
如果用戶對運動沒有意見,按鈕就會向瀏覽器提示變換屬性,并為變換和背景顏色屬性設置了一個過渡。注意這里因為持續時間的不同,所以會有一個漂亮的微妙的交錯效果。
.threeD-button-set button {…@media (--motionOK) {will-change: transform;transition:transform .2s ease,background-color .5s ease;&::before,&::after {transition: transform .1s ease-out;}&::after { transition-duration: .5s }&::before { transition-duration: .3s }} }懸停和聚焦的交互樣式
交互動畫的目標是將構成平面出現的按鈕的各層展開。通過設置–距離變量來實現這一目標,最初是1px。下面代碼示例中的選擇器會檢查按鈕是否被一個應該看到焦點指示器的設備懸停或聚焦,而不是被激活。如果是這樣,它就應用CSS來做以下事情。
- 應用懸停的背景顏色。
- 增加距離。
- 添加一個彈跳緩解效果。
- 錯開偽元素的轉換
不同的過渡時間值只在懸停時才會出現,只為懸停而交錯的動畫。當懸停或焦點移開時,每個層都會一致地過渡到靜止的地方。
.threeD-button-set button {…&:is(:hover, :focus-visible):not(:active) {/* 懸停/聚焦時微妙的距離和bg顏色變化 */--distance: 15px;background-color: var(--_btn-bg-hover);/* 如果運動沒有問題,設置過渡,增加距離 */@media (--motionOK) {--distance: 3vmax;transition-timing-function: var(--_bounce-ease);transition-duration: .4s;&::after { transition-duration: .5s }&::before { transition-duration: .3s }}} }三維視角對于減少運動的偏好來說還是非常整潔的。頂部和底部的元素以一種很好的微妙方式展示了這種效果。
用JavaScript進行的小規模改進
這個界面已經可以通過鍵盤、手柄、觸屏和鼠標來使用,但我們可以添加一些輕微的JavaScript來緩解一些情況。
支持方向鍵
標簽鍵是導航菜單的好方法,但我希望用方向盤或操縱桿來移動游戲板上的焦點。經常用于GUI挑戰界面的roving-ux庫將為我們處理方向鍵。下面的代碼告訴庫在.threeD-button-set中捕獲焦點,并將焦點轉給按鈕的子代。
import {rovingIndex} from 'roving-ux'rovingIndex({element: document.querySelector('.threeD-button-set'),target: 'button', })鼠標視差互動
跟蹤鼠標并讓它傾斜菜單是為了模仿AR和VR視頻游戲界面,在那里你可能有一個虛擬的指針,而不是鼠標。當元素對指針有超強意識時,這可能很有趣。
由于這是一個小的額外功能,我們將把交互放在對用戶的運動偏好的查詢后面。另外,作為設置的一部分,用querySelector將按鈕列表組件存儲到內存中,并將該元素的邊界緩存到menuRect中。使用這些邊界來確定基于鼠標位置應用于卡片的旋轉偏移。
接下來,我們需要一個函數,接受鼠標的x和y位置,并返回一個我們可以用來旋轉卡片的值。下面的函數使用鼠標的位置來確定它在盒子的哪一邊,以及多少。從該函數中返回delta。
const getAngles = (clientX, clientY) => {const { x, y, width, height } = menuRectconst dx = clientX - (x + 0.5 * width)const dy = clientY - (y + 0.5 * height)return {dx,dy} }最后,觀察鼠標的移動,把位置傳給我們的getAngles()函數,并使用delta值作為自定義屬性樣式。我除以20來填充delta,使其不那么抽搐,可能有更好的方法來做。如果你還記得一開始,我們把--x和--y道具放在clamp()函數的中間,這可以防止鼠標位置過度旋轉,使卡片無法辨認。
if (motionOK) {window.addEventListener('mousemove', ({target, clientX, clientY}) => {const {dx,dy} = getAngles(clientX, clientY)menu.attributeStyleMap.set('--x', `${dy / 20}deg`)menu.attributeStyleMap.set('--y', `${dx / 20}deg`)}) }總結
這個項目就到此結束了,如果你還有更好的想法可以在評論區留言,祝大家在新的一年財源滾滾!
總結
以上是生活随笔為你收集整理的手把手教你用CSS实现一个VR 3D游戏菜单栏效果的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2003安装集成SP2补丁+无人值守
- 下一篇: Win8系统的运行哪里找?