Podstawowe informacje o tym, jak stworzyć elastyczne, elastyczne i łatwo dostępne menu gier 3D.
W tym poście chcę pokazać, jak stworzyć komponent menu gry 3D. Wypróbuj demo.
.Jeśli wolisz film, oto wersja tego posta w YouTube:
Omówienie
Gry wideo często mają kreatywne i nietypowe menu, animowane i w przestrzeni 3D. To popularne rozwiązanie w nowych grach AR/VR, ponieważ menu wygląda jak unosi się w przestrzeni kosmicznej. Dzisiaj omówimy podstawy tego efektu, ale dostosowywane kolorystycznie oraz opcje dostosowania którzy preferują mniej ruchu.
HTML
Menu gry to lista przycisków. Najlepszym sposobem pokazania tego w kodzie HTML jest następujące:
<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>
Lista przycisków będzie dobrze widoczna dla technologii czytników ekranu. działa bez JavaScriptu i CSS.
CSS
Styl listy przycisków dzieli się na te ogólne kroki:
- Konfiguruję właściwości niestandardowe.
- Układ flexbox.
- Własny przycisk z ozdobnymi pseudoelementami.
- Umieszczanie elementów w przestrzeni 3D.
Omówienie właściwości niestandardowych
Właściwości niestandardowe pomagają identyfikować wartości, nadając im realistyczne znaczenie w innych przypadkach przypadkowych wartościach, unikając powtarzania kodu i udostępniania wartości wśród dzieci.
Poniżej znajdują się zapytania o media zapisane jako zmienne CSS, znane również jako niestandardowe multimediów. Są to wymiary globalne, używane w różnych selektorach, aby kod był zwięzły i czytelny. Komponent menu gry używa ruchu ustawienia, kolor systemu schemat, i zakres kolorów wyświetlacz.
@custom-media --motionOK (prefers-reduced-motion: no-preference);
@custom-media --dark (prefers-color-scheme: dark);
@custom-media --HDcolor (dynamic-range: high);
Poniższe właściwości niestandardowe zarządzają schematem kolorów i przytrzymują kursor wartości pozycjonowania, dzięki którym menu gry będzie interaktywne i najeżdżać kursorem. Niestandardowe nazwy pomaga zachować czytelność kodu, ponieważ ujawnia przypadek użycia dla wartości lub przyjazna nazwa wyniku dla wartości.
.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);
}
}
}
Tło w kształcie stożka w jasnym i ciemnym motywie
Jasny motyw ma intensywny stożkowy od cyan
do deeppink
gradient
a ciemny ma ciemny, subtelny stożkowy gradient. Aby dowiedzieć się więcej
można użyć gradientów stożkowych, patrz conic.style.
html {
background: conic-gradient(at -10% 50%, deeppink, cyan);
@media (--dark) {
background: conic-gradient(at -10% 50%, #212529, 50%, #495057, #212529);
}
}
Włączanie perspektywy 3D
Aby elementy istniały w przestrzeni 3D strony internetowej, widoczny obszar
z perspektywy
wymaga zainicjowania. Postawiłem perspektywę na element body
i użyliśmy jednostek widocznego obszaru,
aby stworzyć styl, który mi się podoba.
body {
perspective: 40vw;
}
Właśnie taki wpływ może wywrzeć ta perspektywa.
Określanie stylu listy przycisków <ul>
Ten element odpowiada za ogólny układ makra listy przycisków oraz w postaci interaktywnej i pływającej karty 3D. Oto sposób, aby to osiągnąć.
Układ grupy przycisków
Flexbox może zarządzać układem kontenera. Zmień domyślny kierunek Flex
od wierszy do kolumn z wartościami flex-direction
i upewnij się, że rozmiar każdego elementu wynosi
jego zawartość zmienia się z stretch
na start
dla 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;
}
Następnie ustaw kontener jako kontekst przestrzeni 3D i skonfiguruj CSS clamp()
by zapobiec obróceniu karty poza czytelne obroty. Uwaga
że środkowa wartość ograniczenia jest właściwością niestandardową, te --x
i --y
wartości zostaną ustawione w JavaScripcie po najechaniu na nie kursorem myszy
interakcji później.
.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)
)
)
;
}
Następnie, jeśli ruch jest prawidłowy u gościa, dodaj w przeglądarce wskazówkę,
przekształcenie tego elementu będzie się stale zmieniać wraz z
will-change
Aby włączyć interpolację, ustaw też transition
na przekształceniach. Ten
przejście następuje wtedy, gdy mysz wejdzie w interakcję z kartą, umożliwiając płynne
przejścia do zmian rotacji. Jest to animacja wyświetlana przez cały czas.
pokazującą trójwymiarową przestrzeń, w której znajduje się karta, nawet jeśli mysz nie jest w stanie,
nie wchodzi w interakcję z komponentem.
@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;
}
}
Animacja rotate-y
ustawia środkową klatkę kluczową tylko na 50%
, ponieważ
Przeglądarka 0%
i 100%
domyślnie zastosuje domyślny styl elementu. Ten
jest skrótem od animacji naprzemiennych, które muszą zaczynać się i kończyć
pozycji. To świetny sposób na przedstawienie nieskończonych naprzemiennych animacji.
@keyframes rotate-y {
50% {
transform: rotateY(15deg) rotateX(-6deg);
}
}
Określanie stylu elementów <li>
Każdy element listy (<li>
) zawiera przycisk i jego elementy obramowania.
Styl elementu display
został zmieniony i w elemencie nie wyświetla się
::marker
Styl position
jest ustawiona na relative
, więc kolejne pseudoelementy przycisków mogą
na całej powierzchni obszaru wokół przycisku.
.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;
}
Określanie stylu elementów <button>
Stylizowanie przycisków może być pracochłonne. Występuje wiele stanów i typów interakcji które trzeba uwzględnić. Przyciski te szybko się komplikują ze względu na pseudoelementów, animacji i interakcji.
Początkowe style (<button>
)
Poniżej znajduje się lista stylów podstawowych, które będą obsługiwane w innych stanach.
.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;
}
Pseudoelementy przycisku
Krawędzie przycisku to nie tradycyjne obramowanie, tylko pozycja bezwzględna pseudoelementów z obramowaniem.
Te elementy mają kluczowe znaczenie w prezentowaniu perspektywy 3D . Jeden z tych pseudoelementów zostanie odsunięty od przycisku, a jeden z nich będzie bliżej użytkownika. Efekt jest najbardziej zauważalny górnych i dolnych przycisków.
.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);
}
}
}
Style przekształceń 3D
Opcja poniżej transform-style
jest ustawiona na preserve-3d
, aby dzieci mogły przebywać w pokoju
się na osi z
. transform
ma wartość --distance
niestandardową, która zostanie zwiększona po najechaniu kursorem
i zaznacz.
.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));
}
}
Style animacji warunkowych
Jeśli użytkownik wyraża zgodę na ruch, przycisk informuje przeglądarkę, że
właściwość przekształcenia powinna być gotowa do zmiany, a przejście jest ustawione dla
Usługi transform
i background-color
. Zwróć uwagę na różnicę w
wydawało mi się,
że efekt jest delikatny, rozłożony.
.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 }
}
}
Style interakcji z najeżdżaniem kursorem i zaznaczeniem
Celem animacji interakcji jest rozłożenie warstw, z których składa się
płaskiego przycisku. W tym celu ustaw zmienną --distance
.
początkowo do użytkownika 1px
. Selektor widoczny w przykładowym kodzie poniżej sprawdza,
czy urządzenie, które powinno zobaczyć komunikat,
wskaźnik ostrości. Nie została aktywowana. Jeśli tak, korzysta z CSS, aby wykonać
:
- Zastosuj kolor tła po najechaniu kursorem.
- Zwiększ dystans .
- Dodaj efekt łatwego odbijania odbijania.
- Rozłóż przejścia pseudoelementów.
.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 }
}
}
}
Perspektywa 3D nadal świetnie wyglądała w przypadku preferencji ruchu w usłudze reduced
.
Elementy górne i dolne prezentują efekt w subtelny sposób.
Małe ulepszenia w języku JavaScript
Interfejs obsługuje klawiatury, czytniki ekranu, pady do gier, ale możemy dodać kilka drobnych akcentów w JavaScripcie, aby ułatwić dostępnych scenariuszy.
Dodatkowe klawisze strzałek
Klawisz Tab to dobry sposób na poruszanie się po menu, ale tryb kierunkowy
pada lub joysticka, aby przenieść zaznaczenie na pad do gier.
Biblioteka roving-ux często używana w GUI.
Interfejsy wyzwania będą za nas obsługiwać klawisze strzałek. Poniższy kod informuje
zatrzymuje ostrość w obrębie .threeD-button-set
i przekierowuje go na
elementów podrzędnych.
import {rovingIndex} from 'roving-ux'
rovingIndex({
element: document.querySelector('.threeD-button-set'),
target: 'button',
})
Interakcja z paralaksą kursora myszy
Śledzenie myszy i przechylanie menu ma na celu naśladowanie AR i VR. w interfejsach gier wideo, gdzie zamiast myszy może znajdować się wirtualny wskaźnik. Może być zabawnie, gdy elementy są zbyt świadome wskaźnika.
Ponieważ jest to niewielka, dodatkowa funkcja, za pomocą zapytania
zgodnie z preferencjami użytkownika dotyczącymi ruchu. Zapisuj też listę przycisków w ramach konfiguracji
w pamięci za pomocą funkcji querySelector
i zapisują granice elementu w pamięci podręcznej
menuRect
Na podstawie tych granic możesz określić przesunięcie obrotu zastosowane do karty
na podstawie pozycji myszy.
const menu = document.querySelector('.threeD-button-set')
const menuRect = menu.getBoundingClientRect()
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
Następnie potrzebujemy funkcji, która akceptuje pozycje x
i y
myszy oraz zwraca
wartość, której możemy użyć do rotacji karty. Poniższa funkcja używa myszy
aby ustalić, w której części pudełka i jak bardzo się znajduje.
delta jest zwracana z funkcji.
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}
}
Na koniec obserwuj ruch myszy, przekaż informację o pozycji do funkcji getAngles()
i używać wartości delta jako stylów właściwości niestandardowych. dzielę przez 20, aby wypełnić
delta i ograniczania drgań, może być lepsze rozwiązanie. Jeśli
pamiętaj o tym, że rekwizyty --x
i --y
znajdują się w środku
clamp()
, zapobiega to nadmiernemu obracaniu się kursora myszy
w nieczytelne miejsce.
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`)
})
}
Tłumaczenia i wskazówki dojazdu
Podczas testowania menu gry w innych trybach pisania wystąpił błąd. języki.
Elementy <button>
mają styl !important
dla elementu writing-mode
w ustawieniach użytkownika
arkusza stylów agenta. Oznaczało to, że kod HTML menu gry trzeba było zmienić, aby dostosować
zgodnie z oczekiwaniami. Zmiana listy przycisków na listę linków włącza funkcję logiczną
właściwości, które zmieniają kierunek menu, ponieważ w elementach <a>
nie ma przeglądarki
podano styl !important
.
Podsumowanie
Wiesz już, jak to robiłem. Jak to zrobisz‽ 🙂 Czy możesz dodać akcelerometr? interakcji z menu, więc ułożenie kafelków w telefonie spowoduje obrót menu? Czy możemy coś poprawić bez ruchu?
Stosujmy różne podejścia i poznajmy sposoby budowania obecności w internecie. Utwórz wersję demonstracyjną, a potem dodaj linki do funkcji tweetuj mi. znajdziesz poniżej w sekcji z remiksami na karcie Społeczność.
Remiksy utworzone przez społeczność
Na razie nic tu nie ma.