Tổng quan cơ bản về cách tạo trình đơn trò chơi 3D có tính thích ứng, thích ứng và dễ tiếp cận.
Trong bài đăng này, tôi muốn chia sẻ cách tạo thành phần trình đơn trò chơi 3D. Dùng thử bản minh hoạ.
Nếu bạn thích xem video hơn, sau đây là phiên bản của bài đăng này trên YouTube:
Tổng quan
Trò chơi điện tử thường hiển thị cho người dùng một trình đơn sáng tạo và khác thường, có ảnh động và trong không gian 3D. Thực đơn này phổ biến trong các trò chơi thực tế tăng cường/thực tế ảo mới để làm cho trình đơn trông giống như lơ lửng trong không gian. Hôm nay, chúng tôi sẽ tái tạo những yếu tố cơ bản của hiệu ứng này nhưng với sự tinh tế hơn của bảng phối màu thích ứng và tính năng điều chỉnh cho người dùng những người thích chuyển động ít hơn.
HTML
Trình đơn trò chơi là một danh sách các nút. Cách tốt nhất để trình bày mã này trong HTML là sau:
<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>
Một danh sách các nút sẽ tự thông báo rõ ràng cho các công nghệ trình đọc màn hình và hoạt động mà không cần JavaScript hoặc CSS.
CSS
Việc tạo kiểu cho danh sách nút được chia thành các bước tổng quát sau đây:
- Thiết lập thuộc tính tuỳ chỉnh.
- Bố cục hộp linh hoạt.
- Một nút tuỳ chỉnh có các phần tử giả trang trí.
- Đặt các thành phần vào không gian 3D.
Tổng quan về thuộc tính tuỳ chỉnh
Thuộc tính tuỳ chỉnh giúp phân biệt các giá trị bằng cách đưa ra tên thành các giá trị trông ngẫu nhiên, tránh lặp lại mã và chia sẻ các giá trị trong trẻ em.
Dưới đây là các truy vấn phương tiện được lưu dưới dạng biến CSS, còn gọi là tuỳ chỉnh nội dung đa phương tiện. Đây là những xu hướng toàn cầu và sẽ được sử dụng trong nhiều bộ chọn khác nhau để giữ cho mã ngắn gọn và dễ đọc. Chiến lược phát hành đĩa đơn thành phần trình đơn trò chơi sử dụng chuyển động lựa chọn ưu tiên, màu hệ thống hệ thống, và dải màu của màn hình.
@custom-media --motionOK (prefers-reduced-motion: no-preference);
@custom-media --dark (prefers-color-scheme: dark);
@custom-media --HDcolor (dynamic-range: high);
Các thuộc tính tuỳ chỉnh sau quản lý bảng phối màu và giữ chuột giá trị vị trí để tạo trình đơn trò chơi có tính tương tác khi di chuột. Đặt tên tuỳ chỉnh giúp mã dễ đọc vì nó cho biết trường hợp sử dụng của giá trị hoặc tên phù hợp cho kết quả của giá trị.
.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);
}
}
}
Nền hình tròn theo giao diện sáng và tối
Giao diện sáng có màu dao động từ cyan
đến deeppink
âm thanh
chuyển màu
còn giao diện tối có hiệu ứng chuyển màu conic huyền ảo tối. Để xem thêm về
bạn có thể sử dụng hiệu ứng chuyển màu conic, xem conic.style.
html {
background: conic-gradient(at -10% 50%, deeppink, cyan);
@media (--dark) {
background: conic-gradient(at -10% 50%, #212529, 50%, #495057, #212529);
}
}
Bật phối cảnh 3D
Để các phần tử tồn tại trong không gian 3D của một trang web, một chế độ xem có
góc nhìn đa chiều
cần được khởi chạy. Tôi chọn đặt phối cảnh trên phần tử body
và sử dụng các đơn vị khung nhìn để tạo ra kiểu tôi thích.
body {
perspective: 40vw;
}
Đây là loại quan điểm tác động có thể có.
Tạo kiểu danh sách nút <ul>
Phần tử này chịu trách nhiệm về bố cục macro danh sách nút tổng thể cũng như là thẻ nổi 3D và có tính tương tác. Sau đây là cách để đạt được điều đó.
Bố cục nhóm nút
Hộp linh hoạt có thể quản lý bố cục vùng chứa. Thay đổi hướng linh hoạt mặc định
từ các hàng đến cột có flex-direction
và đảm bảo mỗi mục có kích thước bằng
nội dung của nhóm bằng cách thay đổi từ stretch
thành start
cho 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;
}
Tiếp theo, hãy thiết lập vùng chứa làm ngữ cảnh không gian 3D và thiết lập CSS clamp()
chức năng nhằm đảm bảo thẻ không xoay quá mức xoay dễ đọc. Lưu ý
rằng giá trị ở giữa của kẹp là một thuộc tính tuỳ chỉnh, các --x
và --y
này
các giá trị sẽ được đặt từ JavaScript khi di chuột
tương tác sau.
.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)
)
)
;
}
Tiếp theo, nếu người dùng truy cập chấp nhận chuyển động, hãy thêm gợi ý vào trình duyệt
thì sự biến đổi của mục này sẽ liên tục thay đổi với
will-change
.
Ngoài ra, bật nội suy bằng cách đặt transition
trên phép biến đổi. Chiến dịch này
quá trình chuyển đổi sẽ diễn ra khi chuột tương tác với thẻ, cho phép chuyển đổi mượt mà
chuyển đổi sang các thay đổi xoay. Ảnh động là một ảnh động chạy liên tục
biểu thị không gian 3D của thẻ, ngay cả khi chuột không thể hoặc
không tương tác với thành phần này.
@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;
}
}
Ảnh động rotate-y
chỉ đặt khung hình chính ở giữa tại 50%
vì
sẽ mặc định 0%
và 100%
về kiểu mặc định của phần tử. Chiến dịch này
là viết tắt của các hoạt ảnh thay thế, cần bắt đầu và kết thúc cùng một lúc
vị trí. Đây là một cách hay để tạo hiệu ứng chuyển động xen kẽ vô hạn.
@keyframes rotate-y {
50% {
transform: rotateY(15deg) rotateX(-6deg);
}
}
Tạo kiểu cho các phần tử <li>
Mỗi mục trong danh sách (<li>
) chứa nút và các phần tử đường viền của nút đó. Chiến lược phát hành đĩa đơn
Kiểu của display
đã thay đổi nên mục sẽ không hiển thị
::marker
. Kiểu position
được đặt thành relative
để các phần tử giả nút sắp tới có thể định vị
trong toàn bộ vùng mà nút sử dụng.
.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;
}
Tạo kiểu cho các phần tử <button>
Nút tạo kiểu có thể là một công việc khó khăn, có nhiều trạng thái và kiểu tương tác để tính đến. Các nút này nhanh chóng trở nên phức tạp do cần cân bằng phần tử giả, ảnh động và tương tác.
Kiểu <button>
ban đầu
Dưới đây là các kiểu cơ bản sẽ hỗ trợ các trạng thái khác.
.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;
}
Phần tử giả dạng nút
Đường viền của nút không phải là đường viền truyền thống, chúng là vị trí tuyệt đối phần tử giả có đường viền.
Những yếu tố này đóng vai trò quan trọng trong việc hiển thị phối cảnh 3D đã được thiết lập. Một trong những phần tử giả này sẽ được đẩy ra khỏi nút, và một thẻ sẽ được kéo lại gần người dùng hơn. Tác động này dễ nhận thấy nhất ở nút trên cùng và dưới cùng.
.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);
}
}
}
Kiểu biến đổi 3D
Bên dưới transform-style
được đặt thành preserve-3d
để trẻ có thể đặt không gian
trên trục z
. transform
được đặt thành --distance
và sẽ được tăng lên khi di chuột và
.
.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));
}
}
Kiểu ảnh động có điều kiện
Nếu người dùng ổn khi chuyển động, nút này sẽ gợi ý cho trình duyệt biết rằng
thuộc tính biến đổi phải sẵn sàng thay đổi và chuyển đổi đã được thiết lập cho
Thuộc tính transform
và background-color
. Hãy chú ý đến sự khác biệt về
thời lượng của video, tôi cảm thấy nó tạo ra hiệu ứng so le tinh tế.
.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 }
}
}
Kiểu tương tác khi di chuột và tập trung
Mục tiêu của ảnh động tương tác là trải các lớp tạo nên
nút phẳng. Hãy hoàn thành việc này bằng cách đặt biến --distance
,
ban đầu là 1px
. Bộ chọn hiển thị trong các đoạn mã ví dụ sau đây sẽ kiểm tra để
xem liệu thiết bị có đang di chuột hay đặt tiêu điểm vào nút này và bạn sẽ thấy
chỉ báo lấy tiêu điểm, và không được kích hoạt. Nếu có, CSS sẽ thực hiện việc
sau:
- Áp dụng màu nền khi di chuột.
- Tăng khoảng cách .
- Thêm hiệu ứng nhẹ nhàng.
- Xoay vòng chuyển đổi phần tử giả.
.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 }
}
}
}
Góc nhìn 3D vẫn rất gọn gàng đối với lựa chọn chuyển động reduced
.
Các phần tử trên cùng và dưới cùng thể hiện hiệu ứng một cách tinh tế và thú vị.
Các cải tiến nhỏ với JavaScript
Giao diện này có thể sử dụng được từ bàn phím, trình đọc màn hình, tay điều khiển trò chơi, thao tác chạm và nhưng chúng ta có thể thêm một số chi tiết nhỏ cho JavaScript để tình huống cụ thể.
Các phím mũi tên hỗ trợ
Bạn có thể dùng phím tab để di chuyển trong trình đơn một cách hiệu quả, nhưng tôi cho rằng tính năng này sẽ điều hướng
bàn điều khiển hoặc cần điều khiển để di chuyển tiêu điểm trên tay điều khiển trò chơi. Chiến lược phát hành đĩa đơn
thư viện roving-ux thường dùng cho GUI
Giao diện thử thách sẽ xử lý các phím mũi tên. Đoạn mã dưới đây cho biết
thư viện để bẫy tiêu điểm trong .threeD-button-set
và chuyển tiếp tiêu điểm đến
nút con.
import {rovingIndex} from 'roving-ux'
rovingIndex({
element: document.querySelector('.threeD-button-set'),
target: 'button',
})
Tương tác thị sai chuột
Theo dõi chuột và nghiêng trình đơn nhằm bắt chước tính năng Thực tế tăng cường (AR) và Thực tế ảo (VR) giao diện trò chơi điện tử, trong đó bạn có thể dùng con trỏ ảo thay vì dùng chuột. Điều này có thể thú vị khi các phần tử nhận biết rõ ràng về con trỏ.
Vì đây là một tính năng bổ sung nhỏ, nên chúng ta sẽ đặt tương tác sau một truy vấn
sở thích chuyển động của người dùng. Ngoài ra, trong quá trình thiết lập, hãy lưu trữ danh sách nút
thành phần vào bộ nhớ bằng querySelector
và lưu các giới hạn của phần tử vào bộ nhớ đệm
menuRect
Sử dụng các giới hạn này để xác định độ lệch xoay được áp dụng cho thẻ
dựa vào vị trí chuột.
const menu = document.querySelector('.threeD-button-set')
const menuRect = menu.getBoundingClientRect()
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
Tiếp theo, chúng ta cần một hàm chấp nhận vị trí chuột x
và y
rồi trả về
một giá trị mà chúng ta có thể dùng để xoay thẻ. Hàm sau đây sử dụng chuột
để xác định xem nó nằm ở bên nào của hộp và giá trị của nó là bao nhiêu. Chiến lược phát hành đĩa đơn
delta được trả về từ hàm.
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}
}
Cuối cùng, hãy xem thao tác di chuyển của chuột, truyền vị trí vào hàm getAngles()
đồng thời sử dụng các giá trị delta làm kiểu thuộc tính tuỳ chỉnh. Tôi chia cho 20 để đặt
delta và giảm bớt sự phức tạp, có thể có cách tốt hơn để làm việc đó. Nếu bạn
hãy nhớ ngay từ đầu, chúng ta đã đặt đạo cụ --x
và --y
ở giữa
clamp()
, ngăn không cho vị trí chuột xoay quá mức
thẻ vào vị trí khó đọc.
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`)
})
}
Bản dịch và hướng dẫn
Có một lỗi xảy ra khi thử nghiệm trình đơn trò chơi ở các chế độ viết khác và ngôn ngữ.
Các phần tử <button>
có kiểu !important
cho writing-mode
trong người dùng
biểu định kiểu của tác nhân. Tức là HTML của trình đơn trò chơi cần thay đổi cho phù hợp với
thiết kế mong muốn. Việc thay đổi danh sách nút thành danh sách đường liên kết sẽ cho phép
các thuộc tính để thay đổi hướng trình đơn, vì các phần tử <a>
không có trình duyệt
đã cung cấp kiểu !important
.
Kết luận
Giờ bạn đã biết cách tôi thực hiện việc đó, bạn sẽ làm cách nào‽ 🙂 Bạn có thể thêm gia tốc kế không tương tác với trình đơn, vì vậy, việc xếp điện thoại sẽ xoay trình đơn? Chúng tôi có thể cải thiện không trải nghiệm không chuyển động?
Hãy đa dạng hoá phương pháp tiếp cận và tìm hiểu tất cả các cách xây dựng ứng dụng trên web. Tạo một bản minh hoạ, tweet cho tôi các đường liên kết và tôi sẽ thêm vào vào phần bản phối lại của cộng đồng dưới đây!
Bản phối lại của cộng đồng
Chưa có gì để xem ở đây!