Descripción general de los conceptos básicos sobre cómo crear un menú de juego en 3D responsivo, adaptable y accesible
En esta publicación, quiero contarte cómo crear un componente del menú de un juego en 3D. Prueba la demostración.
Si prefieres ver un video, aquí tienes una versión de YouTube de esta publicación:
Descripción general
Los videojuegos suelen presentar a los usuarios un menú creativo e inusual, animado y en un espacio 3D. En los juegos de RA y RV, es popular hacer que el menú parezca estar flotando en el espacio. Hoy recrearemos los elementos esenciales de este efecto, con el estilo adicional de un esquema de colores adaptable y adaptaciones para los usuarios que prefieren menos movimiento.
HTML
El menú del juego es una lista de botones. La mejor manera de representar esto en HTML es como sigue:
<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>
Una lista de botones se anunciará bien para las tecnologías de lectores de pantalla y funciona sin JavaScript ni CSS.
CSS
El diseño de la lista de botones se divide en los siguientes pasos de alto nivel:
- Configurar propiedades personalizadas
- Un diseño de flexbox
- Un botón personalizado con pseudoelementos decorativos.
- Colocación de elementos en un espacio 3D
Descripción general de las propiedades personalizadas
Las propiedades personalizadas ayudan a desambiguar valores, ya que brindan nombres a valores de aspecto aleatorio, lo que evita el uso entre los niños.
A continuación, se muestran las consultas de medios guardadas como variables de CSS, también conocidas como valores personalizados contenido multimedia. Son globales y se usará en varios selectores para que el código sea conciso y legible. El El componente del menú del juego usa movimiento preferencias, color del sistema esquema, y rango de color capacidades de la pantalla.
@custom-media --motionOK (prefers-reduced-motion: no-preference);
@custom-media --dark (prefers-color-scheme: dark);
@custom-media --HDcolor (dynamic-range: high);
Las siguientes propiedades personalizadas administran el esquema de colores y mantienen presionado el mouse. valores posicionales para que se pueda colocar el cursor sobre el menú del juego de forma interactiva. Asignación de nombres personalizada propiedades ayudan a la legibilidad del código, ya que revela el caso de uso del valor o un nombre descriptivo para el resultado del valor.
.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);
}
}
}
Fondos cónicos del fondo del tema claro y oscuro
El tema claro tiene un tono cónico de cyan
a deeppink
de colores intensos
gradiente
mientras que el tema oscuro tiene un gradiente cónico sutil oscuro. Para ver más sobre qué
esto se puede hacer con gradientes cónicos, consulta conic.style.
html {
background: conic-gradient(at -10% 50%, deeppink, cyan);
@media (--dark) {
background: conic-gradient(at -10% 50%, #212529, 50%, #495057, #212529);
}
}
Cómo habilitar la perspectiva 3D
Para que los elementos existan en el espacio 3D de una página web, un viewport con
perspectiva
debe inicializarse. Elegí poner la perspectiva en el elemento body
.
y usé unidades de viewport para crear el estilo que me gustaba.
body {
perspective: 40vw;
}
Este es el tipo de perspectiva de impacto que puede tener.
Cómo aplicar diseño a la lista de botones <ul>
Este elemento es responsable del diseño general de la macro de la lista de botones, así como del es una tarjeta interactiva y flotante en 3D. Aquí te mostramos una manera de lograrlo.
Diseño del grupo de botones
Flexbox puede administrar el diseño del contenedor. Cambiar la dirección predeterminada de Flex
de filas a columnas con flex-direction
y asegúrate de que cada elemento tenga el tamaño de
su contenido cambiando de stretch
a start
para align-items
.threeD-button-set {
/* remove <ul> margins */
margin: 0;
/* vertical rag-right layout */
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 2.5vh;
}
Luego, establece el contenedor como un contexto de espacio 3D y configura el clamp()
del CSS.
para garantizar que la tarjeta no rote más allá de rotaciones legibles. Aviso
que el valor medio de la abrazadera es una propiedad personalizada, estos --x
y --y
los valores se establecerán desde JavaScript con el mouse
interacción más adelante.
.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)
)
)
;
}
Luego, si el movimiento del usuario es aceptable, agrega una sugerencia al navegador de que
la transformación de este elemento
cambiará constantemente con
will-change
Además, habilita la interpolación estableciendo un transition
en las transformaciones. Esta
cuando el mouse interactúe con la tarjeta, lo que permite
las transiciones a los cambios de rotación. La animación es una animación de ejecución constante
que demuestre el espacio 3D dentro de la tarjeta, incluso si un mouse no puede
no está interactuando con el componente.
@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;
}
}
La animación rotate-y
solo establece el fotograma clave del medio en 50%
, ya que el
el navegador aplicará 0%
y 100%
de forma predeterminada al estilo predeterminado del elemento. Esta
es una abreviatura de animaciones que se alternan, necesitan comenzar y terminar de la misma manera
posición. Es una excelente manera de articular animaciones alternadas infinitas.
@keyframes rotate-y {
50% {
transform: rotateY(15deg) rotateX(-6deg);
}
}
Cómo aplicar diseño a los elementos <li>
Cada elemento de la lista (<li>
) contiene el botón y sus elementos de borde. El
Se cambió el estilo de display
para que el elemento no muestre un
::marker
Estilo position
se configura en relative
para que los próximos seudoelementos del botón puedan posicionarse
dentro del área completa que consume el botón.
.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;
}
Cómo aplicar diseño a los elementos <button>
Diseñar los botones puede ser un trabajo difícil, hay muchos estados y tipos de interacción. tener en cuenta. Estos botones se vuelven complejos rápidamente debido al equilibrio seudoelementos, animaciones e interacciones.
<button>
diseños iniciales
A continuación, se muestran los estilos básicos que admitirán los otros estados.
.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;
}
Pseudoelementos de botones
Los bordes del botón no son tradicionales, son una posición absoluta. seudoelementos con bordes.
Estos elementos son cruciales para mostrar la perspectiva 3D que se ha establecidos. Uno de estos seudoelementos será alejado del botón, y uno se acercará al usuario. El efecto es más evidente en la inferior y superior.
.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);
}
}
}
Estilos de transformación 3D
El valor inferior a transform-style
se estableció en preserve-3d
para que los elementos secundarios puedan agregar espacios
en el eje z
. transform
se establece en --distance
.
propiedad personalizada, que aumentará cuando coloque el cursor y
enfoque.
.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));
}
}
Estilos de animación condicionales
Si el usuario está de acuerdo con el movimiento, el botón sugiere al navegador que
de transformación debe estar lista para el cambio y se debe establecer una transición para
Propiedades transform
y background-color
. Observa la diferencia en
me pareció que generaba un bonito efecto escalonado.
.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 }
}
}
Estilos de interacción de colocar el cursor sobre un elemento y enfocar
El objetivo de la animación de interacción es extender las capas que componen la
botón de aparición plana. Para lograrlo, configura la variable --distance
.
inicialmente en 1px
. El selector que se muestra en el siguiente ejemplo de código verifica
ver si un dispositivo que debe colocar el cursor sobre el botón o lo enfoca
un indicador de enfoque y no estar activado. Si es así, aplica CSS para hacer lo siguiente:
lo siguiente:
- Aplica el color de fondo cuando se coloca el cursor sobre un elemento.
- Aumenta la distancia .
- Agrega un efecto de aceleración de rebote.
- Escalonar las transiciones de los seudoelementos.
.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 }
}
}
}
La perspectiva 3D seguía siendo muy buena para la preferencia de movimiento de reduced
.
Los elementos inferior y superior muestran el efecto de una manera agradable y sutil.
Pequeñas mejoras con JavaScript
La interfaz se puede utilizar desde teclados, lectores de pantalla, controles de juegos, controles táctiles y del mouse, pero podemos agregar un poco de JavaScript para facilitar diferentes situaciones.
Cómo brindar compatibilidad con teclas de flecha
La tecla Tab es una buena forma de navegar por el menú, pero esperaría que la tecla
o joysticks para mover el enfoque en un control de mando. El
Biblioteca roving-ux que se usa a menudo para la GUI
Las interfaces de desafío se encargarán de las teclas de flecha por nosotros. El siguiente código le indica
para capturar el enfoque dentro de .threeD-button-set
y reenviarlo al
botón secundario.
import {rovingIndex} from 'roving-ux'
rovingIndex({
element: document.querySelector('.threeD-button-set'),
target: 'button',
})
Interacción con el paralaje del mouse
Hacer un seguimiento del mouse e inclinar el menú está diseñado para imitar la RA y la RV interfaces de videojuegos, con las que, en lugar de un mouse, puedes tener un puntero virtual. Puede ser divertido cuando los elementos conocen muy bien el puntero.
Como se trata de un atributo adicional pequeño, pondremos la interacción detrás de una consulta de
la preferencia de movimiento del usuario. Además, como parte de la configuración, debes almacenar la lista de botones
en la memoria con querySelector
y almacenar en caché los límites del elemento en
menuRect
Usa estos límites para determinar el desplazamiento de rotación que se aplica a la tarjeta
según la posición del mouse.
const menu = document.querySelector('.threeD-button-set')
const menuRect = menu.getBoundingClientRect()
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
A continuación, necesitamos una función que acepte las posiciones x
y y
del mouse, y muestre
un valor que podemos usar para rotar la tarjeta. La siguiente función usa el mouse
para identificar en qué lado
de la caja se encuentra y en qué medida. El
se devuelve como delta desde la función.
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}
}
Por último, observa cómo se mueve el mouse y pasa la posición a nuestra función getAngles()
.
y usar los valores delta como estilos de propiedad personalizada. Dividí por 20 para rellenar
o hacer que sea menos trabas, existe una mejor manera de hacerlo. Si
Recuerda que, desde el principio, pusimos los objetos --x
y --y
en medio de un
función clamp()
, evita que la posición del mouse gire demasiado el
la tarjeta en una posición ilegible.
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`)
})
}
Traducciones e instrucciones sobre cómo llegar
Hubo una dificultad al probar el menú del juego en otros modos de escritura y idiomas.
Los elementos <button>
tienen un estilo !important
para writing-mode
en el usuario.
hoja de estilo del agente. Esto significaba que se debía cambiar el código HTML del menú del juego para adaptarse a
el diseño deseado. Cambiar la lista de botones a una lista de vínculos habilita la
propiedades para cambiar la dirección del menú, ya que los elementos <a>
no tienen navegador
estilo !important
proporcionado.
Conclusión
Ahora que sabes cómo lo hice, ¿cómo lo harías?‽ 🙂 ¿Puedes agregar un acelerómetro interacción con el menú, entonces, ¿el menú se rota al mosaico del teléfono? ¿Podemos mejorar? la experiencia sin movimiento?
Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web. Crear una demostración, twittearme vínculos y la agregaré. a la sección de remixes de la comunidad.
Remixes de la comunidad
Aún no hay nada para ver aquí.