关于如何构建响应式、自适应且易于访问的 3D 游戏菜单的基础知识。
在这篇博文中,我想分享一下如何构建 3D 游戏菜单组件。试用 演示。
<ph type="x-smartling-placeholder">如果你更喜欢视频,可以参阅此博文的 YouTube 版本:
概览
视频游戏经常会为用户提供创意十足的与众不同的菜单,例如动画 和 3D 空间在新的 AR/VR 游戏中,菜单很受欢迎 漂浮在太空中的今天,我们将重现此特效的基本功能 增添了自适应配色方案,并为用户提供了可调节的装饰 并且更喜欢减少动作
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>
一系列按钮将能很好地向屏幕阅读器技术和 无需 JavaScript 或 CSS 即可正常运行
CSS
设置按钮列表的样式可细分为以下几个高级步骤:
- 设置自定义属性。
- Flexbox 布局。
- 包含装饰性伪元素的自定义按钮。
- 将元素放入 3D 空间。
自定义属性概览
自定义属性通过提供有意义的值来消除值的歧义 名称转换为随机值,避免重复代码和共享 值。
下面是保存为 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);
}
}
}
浅色和深色主题背景圆锥形背景
浅色主题具有鲜艳的 cyan
到 deeppink
圆锥形
梯度
而深色主题则具有暗淡细微的圆锥形渐变。想要详细了解
也可以使用圆锥渐变,请参阅 conic.style。
html {
background: conic-gradient(at -10% 50%, deeppink, cyan);
@media (--dark) {
background: conic-gradient(at -10% 50%, #212529, 50%, #495057, #212529);
}
}
启用 3D 透视
要让元素存在于网页的 3D 空间中,必须具有
观点
需要初始化。我选择从不同角度来看待body
元素
然后使用视口单元创建了我喜欢的样式
body {
perspective: 40vw;
}
这是视角可以产生的影响类型。
设置 <ul>
按钮列表的样式
此元素负责整个按钮列表宏布局以及 这是一种交互式 3D 浮动卡片以下是实现这一目标的方法。
按钮组布局
Flexbox 可以管理容器布局。更改 flex 的默认方向
使用 flex-direction
从行到列,并确保每一项的大小都是
方法是将 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;
}
接下来,将容器设置为 3D 空间上下文,并设置 CSS clamp()
函数,确保卡片在旋转后不会超出可辨认的旋转角度。通知
该取值范围的中间值是自定义属性,即 --x
和 --y
值将通过鼠标通过 JavaScript 设置
互动。
.threeD-button-set {
…
/* create 3D space context */
transform-style: preserve-3d;
/* clamped menu rotation to not be too extreme */
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
。
此外,您还可以通过在转换上设置 transition
来启用插值。这个
当鼠标与卡片互动时,就会发生平滑过渡
过渡到旋转变化。该动画是一个不断运行的动画
以及卡片中的 3D 空间
没有与组件互动
@media (--motionOK) {
.threeD-button-set {
/* browser hint so it can be prepared and optimized */
will-change: transform;
/* transition transform style changes and run an infinite animation */
transition: transform .1s ease;
animation: rotate-y 5s ease-in-out infinite;
}
}
rotate-y
动画仅将中间关键帧设置为 50%
,因为
浏览器会将 0%
和 100%
默认设为元素的默认样式。这个
是交替动画的简写形式,开始和结束时间相同
排名。这是清晰地呈现无限的交替动画的好方法。
@keyframes rotate-y {
50% {
transform: rotateY(15deg) rotateX(-6deg);
}
}
设置 <li>
元素的样式
每个列表项 (<li>
) 都包含按钮及其边框元素。通过
display
样式已更改,因此该项不显示
::marker
。position
样式
设置为 relative
,以便后续的按钮伪元素可以放置
按钮消耗的全部区域内。
.threeD-button-set > li {
/* change display type from list-item */
display: inline-flex;
/* create context for button pseudos */
position: relative;
/* create 3D space context */
transform-style: preserve-3d;
}
设置 <button>
元素的样式
设置按钮样式可能会很困难,有许多状态和互动类型 由于存在平衡机制,这些按钮很快就会变得复杂 伪元素、动画和交互。
初始<button>
样式
以下是可支持其他状态的基础样式。
.threeD-button-set button {
/* strip out default button styles */
appearance: none;
outline: none;
border: none;
/* bring in brand styles via props */
background-color: var(--_btn-bg);
color: var(--_btn-text);
text-shadow: 0 1px 1px var(--_btn-text-shadow);
/* large text rounded corner and padded*/
font-size: 5vmin;
font-family: Audiowide;
padding-block: .75ch;
padding-inline: 2ch;
border-radius: 5px 20px;
}
按钮伪元素
按钮的边框不是传统的边框,而是绝对位置 伪元素。
这些元素对于展示过去 3D 视角 。其中一个伪元素将被推离按钮, 其中一个广告会更靠近用户这一效果在 顶部和底部按钮。
.threeD-button button {
…
&::after,
&::before {
/* create empty element */
content: '';
opacity: .8;
/* cover the parent (button) */
position: absolute;
inset: 0;
/* style the element for border accents */
border: 1px solid var(--theme);
border-radius: 5px 20px;
}
/* exceptions for one of the pseudo elements */
/* this will be pushed back (3x) and have a thicker border */
&::before {
border-width: 3px;
/* in dark mode, it glows! */
@media (--dark) {
box-shadow:
0 0 25px var(--theme),
inset 0 0 25px var(--theme);
}
}
}
3D 转换样式
在 transform-style
下,设置为 preserve-3d
,以便子项可以间距
z
轴上。transform
设置为 --distance
。
自定义属性,该属性的值在悬停和
关注。
.threeD-button-set button {
…
transform: translateZ(var(--distance));
transform-style: preserve-3d;
&::after {
/* pull forward in Z space with a 3x multiplier */
transform: translateZ(calc(var(--distance) / 3));
}
&::before {
/* push back in Z space with a 3x multiplier */
transform: translateZ(calc(var(--distance) / 3 * -1));
}
}
条件动画样式
如果用户没有动作,该按钮会提示浏览器
transform 属性应已准备好进行更改,并为
transform
和 background-color
属性。请注意
我觉得这首歌巧妙地产生了一种微妙的交错效果
.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 }
}
}
悬停和聚焦互动样式
互动动画的目标是分散各层构成
一个平面按钮可通过设置 --distance
变量来实现这一点,
初始设置为 1px
。以下代码示例中显示的选择器会检查
查看设备是否悬停或聚焦于该按钮上应显示
焦点指示符,且未激活。如果是这样,它会应用 CSS
以下:
- 应用悬停背景颜色。
- 增加距离 。
- 添加弹跳缓和效果。
- 错开伪元素转换。
.threeD-button-set button {
…
&:is(:hover, :focus-visible):not(:active) {
/* subtle distance plus bg color change on hover/focus */
--distance: 15px;
background-color: var(--_btn-bg-hover);
/* if motion is OK, setup transitions and increase distance */
@media (--motionOK) {
--distance: 3vmax;
transition-timing-function: var(--_bounce-ease);
transition-duration: .4s;
&::after { transition-duration: .5s }
&::before { transition-duration: .3s }
}
}
}
对于 reduced
动作偏好设置,3D 视角仍然非常简洁。
顶部和底部的元素以一种巧妙的方式展现效果。
使用 JavaScript 进行小幅改进
可通过键盘、屏幕阅读器、游戏手柄、触控和 但我们可以添加一些简单的 JavaScript 代码, 场景。
支持箭头键
使用 Tab 键可以很方便地在菜单中导航,
或操纵杆来将焦点移到游戏手柄上。通过
通常用于 GUI 的 roving-ux 库
挑战界面将为我们处理箭头键。下面的代码告诉
库将焦点限制在 .threeD-button-set
内,并将焦点转发到
。
import {rovingIndex} from 'roving-ux'
rovingIndex({
element: document.querySelector('.threeD-button-set'),
target: 'button',
})
鼠标视差互动
跟踪鼠标并使菜单倾斜,旨在模拟 AR 和 VR 视频游戏界面中,可以使用虚拟指针来代替鼠标。 当元素超感知指针时,这可能会很有趣。
由于这是一个很小的额外功能,因此我们将互动操作置于以下查询之后:
用户的动作偏好设置。此外,在设置过程中,请存储按钮列表
使用 querySelector
将组件放入内存中,并将元素的边界缓存到
menuRect
。使用这些边界来确定应用于卡片的旋转偏移量
根据鼠标位置调整图片。
const menu = document.querySelector('.threeD-button-set')
const menuRect = menu.getBoundingClientRect()
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
接下来,我们需要一个函数,它接受鼠标 x
和 y
位置并返回
我们可用来旋转卡片的值以下函数使用鼠标
以确定它位于盒子的哪一边以及大小。通过
函数返回 delta。
const getAngles = (clientX, clientY) => {
const { x, y, width, height } = menuRect
const dx = clientX - (x + 0.5 * width)
const dy = clientY - (y + 0.5 * height)
return {dx,dy}
}
最后,观察鼠标移动,将位置传递给 getAngles()
函数
并将增量值用作自定义属性样式。我除以 20 就是
并减少抖动,可能有更好的方法。如果您
请注意,我们从一开始就将 --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`)
})
}
翻译和路线
在其他编写模式下测试游戏菜单时,有一个问题, 语言。
对于用户中的 writing-mode
,<button>
元素具有 !important
样式
代理样式表。这意味着需要更改游戏菜单 HTML 以适应
期望的设计将按钮列表更改为链接列表可启用逻辑
属性来更改菜单方向,因为 <a>
元素没有浏览器
提供了 !important
样式。
总结
现在您已经知道我是怎么做的了,您怎么办 ‽ 🙂? 您能添加加速度计吗 与菜单进行的互动,是否平铺手机会旋转菜单?我们可以改进吗 无动作体验?
让我们一起采用多样化的方法,学习所有在 Web 上构建应用的方法。 创建演示,在 Twitter 微博上添加链接,然后我会添加 到下面的社区混剪部分!
社区混剪作品
此处尚无任何可显示的内容!