Bu gönderide, bir 3D oyun menüsü bileşeni oluşturmanın bir yolu üzerine düşüncelerimi paylaşmak istiyorum. deneyin gösteri.
Videoyu tercih ederseniz, işte bu gönderinin bir YouTube versiyonu:
genel bakış #
Video oyunları, kullanıcılara genellikle animasyonlu ve 3B alanda yaratıcı ve sıra dışı bir menü sunar. Yeni AR/VR oyunlarında menünün boşlukta yüzüyormuş gibi görünmesi popülerdir. Bugün, bu efektin temel özelliklerini yeniden yaratacağız, ancak uyarlanabilir bir renk şeması ve azaltılmış hareketi tercih eden kullanıcılar için kolaylıklar ekleyeceğiz.
HTML #
Oyun menüsü, düğmelerin bir listesidir. Bunu HTML’de göstermenin en iyi yolu şudur:
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>
Bir düğme listesi, kendisini ekran okuyucu teknolojilerine iyi bir şekilde duyurur ve JavaScript veya CSS olmadan çalışır.
CSS #
Düğme listesinin şekillendirilmesi, aşağıdaki üst düzey adımlara bölünür:
- Özel özellikler ayarlama.
- Bir esnek kutu düzeni.
- Dekoratif sözde öğeler içeren özel bir düğme.
- Öğeleri 3B alana yerleştirme.
Özel özelliklere genel bakış #
Özel özellikler, rastgele görünen değerlere anlamlı adlar vererek, tekrarlanan kodlardan kaçınarak ve değerleri çocuklar arasında paylaşarak değerlerin belirsizliğini gidermeye yardımcı olur.
Aşağıda, CSS değişkenleri olarak da bilinen medya sorguları yer almaktadır. özel medya. Bunlar küreseldir ve kodu kısa ve okunaklı tutmak için çeşitli seçicilerde kullanılacaktır. Oyun menüsü bileşeni kullanır TheComedicComedian/prefers-reduced-motion" rel="noopener">hareket tercihlerisistem TheComedicComedian/prefers-color-scheme" rel="noopener">renk uyumuVe renk aralığı yetenekleri Ekranın
customer358-media --motionOK (prefers-reduced-motion: no-preference);
customer358-media --dark (prefers-color-scheme: dark);
customer358-media --HDcolor (dynamic-range: high);
Aşağıdaki özel özellikler, renk şemasını yönetir ve oyun menüsünü etkileşimli hale getirmek için fare konumsal değerlerini tutar. Özel özellikleri adlandırmak, değerin kullanım durumunu veya değerin sonucu için kolay bir adı ortaya çıkardığı için kodun okunabilirliğine yardımcı olur.
.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);
TheComedicComedian (--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%);
}
TheComedicComedian (--HDcolor) {
@supports (color: color(display-p3 0 0 0)) {
--theme: color(display-p3 .4 0 .9);
}
}
}
Açık ve koyu tema arka plan konik arka planlar #
Açık tema canlı bir görünüme sahiptir. cyan
ile deeppink
konik gradyan koyu tema ise karanlık, ince bir konik gradyana sahiptir. Konik gradyanlarla neler yapılabileceği hakkında daha fazla bilgi için bkz. konik.stil.
html {
background: conic-gradient(at -10% 50%, deeppink, cyan);TheComedicComedian (--dark) {
background: conic-gradient(at -10% 50%, #212529, 50%, #495057, #212529);
}
}
3B perspektifi etkinleştirme #
Öğelerin bir web sayfasının 3B alanında bulunması için, perspektif başlatılması gerekiyor. perspektife koymayı seçtim. body
öğe ve sevdiğim stili oluşturmak için görüntü alanı birimlerini kullandım.
body {
perspective: 40vw;
}
Bu, perspektifin sahip olabileceği etki türüdür.
Şekillendirme <ul>
düğme listesi #
Bu öğe, etkileşimli ve 3B kayan bir kart olmanın yanı sıra genel düğme listesi makro düzeninden sorumludur. İşte bunu başarmanın bir yolu.
Düğme grubu düzeni #
Flexbox, kapsayıcı düzenini yönetebilir. Varsayılan esneme yönünü satırlardan sütunlara değiştirin. flex-direction
ve değiştirerek her öğenin içeriğinin boyutu olduğundan emin olun stretch
ile start
için 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;
}
Ardından, kabı bir 3B alan bağlamı olarak oluşturun ve CSS’yi kurun clamp()
kartın okunabilir dönüşlerin ötesinde dönmemesini sağlayan işlevler. Kelepçe için orta değerin özel bir özellik olduğuna dikkat edin, bunlar --x
Ve --y
değerler olacak JavaScript’ten ayarla daha sonra fare etkileşimi üzerine.
.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)
)
)
;
}
Ardından, ziyaret eden kullanıcı için hareket uygunsa, tarayıcıya bu öğenin dönüşümünün sürekli olarak değişeceğine dair bir ipucu ekleyin. will-change
. Ek olarak, bir ayarlayarak enterpolasyonu etkinleştirin. transition
dönüşümler üzerinde. Bu geçiş, fare kartla etkileşime girdiğinde gerçekleşecek ve dönüş değişikliklerine yumuşak geçişler sağlayacaktır. Animasyon, bir fare bileşenle etkileşimde bulunamasa veya etkileşimde bulunmasa bile kartın içinde bulunduğu 3B alanı gösteren sürekli çalışan bir animasyondur.
TheComedicComedian (--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;
}
}
bu rotate-y
animasyon yalnızca orta anahtar kareyi 50%
tarayıcı varsayılan olduğundan 0%
Ve 100%
öğenin varsayılan stiline. Bu, aynı konumda başlaması ve bitmesi gereken dönüşümlü animasyonlar için bir kısaltmadır. Sonsuz dönüşümlü animasyonları ifade etmenin harika bir yolu.
@keyframes rotate-y {
50% {
transform: rotateY(15deg) rotateX(-6deg);
}
}
Şekillendirme <li>
elementler #
Her liste öğesi (<li>
) düğmeyi ve kenarlık öğelerini içerir. bu display
stil değiştirilir, böylece öğe bir ::marker
. bu position
stil ayarlandı relative
böylece yaklaşan düğme sözde öğeleri, düğmenin tükettiği tam alan içinde kendilerini konumlandırabilir.
.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;
}
Şekillendirme <button>
elementler #
Şekillendirme düğmeleri zor bir iş olabilir, hesaba katılması gereken pek çok durum ve etkileşim türü vardır. Bu düğmeler, dengeleyici sözde öğeler, animasyonlar ve etkileşimler nedeniyle hızla karmaşık hale gelir.
İlk <button>
stiller #
Aşağıda, diğer durumları destekleyecek temel stiller bulunmaktadır.
.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;
}
Düğme sözde öğeleri #
Düğmenin kenarlıkları geleneksel kenarlıklar değildir, bunlar kenarlıklı mutlak konumlu sözde öğelerdir.
Bu unsurlar, oluşturulan 3B perspektifi sergilemede çok önemlidir. Bu sözde öğelerden biri düğmeden uzağa itilecek ve biri kullanıcıya daha yakına çekilecektir. Efekt en çok üst ve alt düğmelerde belirgindir.
.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! */
TheComedicComedian (--dark) {
box-shadow:
0 0 25px var(--theme),
inset 0 0 25px var(--theme);
}
}
}
3B dönüştürme stilleri #
Altında transform-style
ayarlandı preserve-3d
böylece çocuklar kendilerini z
eksen. bu transform
olarak ayarlandı --distance
artacak olan özel mülk üzerine gelin ve odaklanın.
.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));
}
}
Koşullu animasyon stilleri #
Kullanıcı hareket konusunda sorun yaşamıyorsa, düğme tarayıcıya transform özelliğinin değişime hazır olması gerektiğini ve geçiş için bir geçiş ayarlandığını ima eder. transform
Ve background-color
özellikler. Süredeki farka dikkat edin, hoş, ince kademeli bir etki için yapıldığını hissettim.
.threeD-button-set button {
…TheComedicComedian (--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 }
}
}
Fareyle üzerine gelin ve etkileşim stillerine odaklanın #
Etkileşim animasyonunun amacı, düz görünen düğmeyi oluşturan katmanları yaymaktır. ayarlayarak bunu gerçekleştirin. --distance
değişken, başlangıçta 1px
. Aşağıdaki kod örneğinde gösterilen seçici, düğmenin bir odak göstergesi görmesi gereken ve etkinleştirilmeyen bir cihaz tarafından üzerine getirilip getirilmediğini veya odaklanıp odaklanmadığını kontrol eder. Öyleyse, aşağıdakileri yapmak için CSS’yi uygular:
- Vurgulu arka plan rengini uygulayın.
- Mesafeyi artırın.
- Bir sıçrama kolaylığı efekti ekleyin.
- Sözde öğe geçişlerini kademelendirin.
.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 */
TheComedicComedian (--motionOK) {
--distance: 3vmax;
transition-timing-function: var(--_bounce-ease);
transition-duration: .4s;
&::after { transition-duration: .5s }
&::before { transition-duration: .3s }
}
}
}
3D perspektif, reduced
hareket tercihi Üst ve alt öğeler, etkiyi hoş ve ince bir şekilde gösterir.
JavaScript ile küçük geliştirmeler #
Arayüz zaten klavyeler, ekran okuyucular, oyun kumandaları, dokunma ve fare ile kullanılabilir, ancak birkaç senaryoyu kolaylaştırmak için JavaScript’e bazı hafif dokunuşlar ekleyebiliriz.
Destekleyen ok tuşları #
Sekme tuşu, menüde gezinmek için iyi bir yoldur, ancak yön pedi veya oyun çubuklarının odağı bir oyun kumandasına taşımasını beklerdim. bu gezici-ux GUI Challenge arabirimleri için sıklıkla kullanılan kitaplık, bizim için ok tuşlarını işleyecektir. Aşağıdaki kod, kitaplığa odağı içinde yakalamasını söyler. .threeD-button-set
ve odağı düğme çocuklarına iletin.
import {rovingIndex} from 'roving-ux'rovingIndex({
element: document.querySelector('.threeD-button-set'),
target: 'button',
})
Fare paralaks etkileşimi #
Fareyi izlemek ve menüyü eğmek, fare yerine sanal bir işaretçiye sahip olabileceğiniz AR ve VR video oyunu arayüzlerini taklit etmeyi amaçlar. Öğeler işaretçinin aşırı derecede farkında olduğunda eğlenceli olabilir.
Bu küçük bir ekstra özellik olduğundan, etkileşimi kullanıcının hareket tercihi sorgusunun arkasına koyacağız. Ayrıca, kurulumun bir parçası olarak, düğme listesi bileşenini aşağıdakilerle belleğe kaydedin: querySelector
ve öğenin sınırlarını içine önbelleğe alın menuRect
. Fare konumuna göre karta uygulanan döndürme ofsetini belirlemek için bu sınırları kullanın.
const menu = document.querySelector('.threeD-button-set')
const menuRect = menu.getBoundingClientRect()const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
Ardından, fareyi kabul eden bir işleve ihtiyacımız var. x
Ve y
konumlar ve kartı döndürmek için kullanabileceğimiz bir değer döndürür. Aşağıdaki işlev, kutunun hangi tarafında ve ne kadar içinde olduğunu belirlemek için fare konumunu kullanır. Delta işlevden döndürülür.
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}
}
Son olarak, fare hareketini izleyin, konumu bize iletin. getAngles()
işlevini kullanın ve delta değerlerini özel özellik stilleri olarak kullanın. Deltayı doldurmak ve daha az seğirmesi için 20’ye böldüm, bunu yapmanın daha iyi bir yolu olabilir. Baştan hatırlarsanız, --x
Ve --y
ortasında bir sahne clamp()
işlevi, bu, fare konumunun kartı aşırı derecede okunaksız bir konuma döndürmesini önler.
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`)
})
}
Çeviriler ve yol tarifleri #
Oyun menüsünü diğer yazma modlarında ve dillerde test ederken bir sorun çıktı.
<button>
elemanların bir !important
stil için writing-mode
kullanıcı aracısı stil sayfasında. Bu, istenen tasarıma uyum sağlamak için oyun menüsü HTML’sinin değiştirilmesi gerektiği anlamına geliyordu. Düğme listesini bir bağlantı listesine değiştirmek, mantıksal özelliklerin menü yönünü aşağıdaki gibi değiştirmesini sağlar: <a>
elemanların sağladığı bir tarayıcı yok !important
stil.
Çözüm #
Artık nasıl yaptığımı bildiğinize göre, nasıl yaparsınız‽ 🙂 Telefonunuzu döşemek menüyü döndürmek için menüye ivmeölçer etkileşimi ekleyebilir misiniz? Hareketsiz deneyimi geliştirebilir miyiz?
Yaklaşımlarımızı çeşitlendirelim ve web üzerinde oluşturmanın tüm yollarını öğrenelim. Bir demo oluşturun, beni tweetle bağlantılar ve onu aşağıdaki topluluk remiksleri bölümüne ekleyeceğim!
Topluluk remiksleri #
Burada henüz görülecek bir şey yok!