用螢幕寬度推測輸入方式是最普遍也最錯誤的假設。1366px 寬可能是筆電(滑鼠)、可能是觸控筆電(手指 + 滑鼠)、可能是橫放的平板(手指)、也可能是接了鍵盤的平板(手指 + 鍵盤)。Media Queries Level 4 提供的 pointer 與 hover 直接查詢輸入能力,這才是正確的判斷依據。
查詢輸入能力
| 查詢 | 值 | 意義 |
|---|---|---|
pointer | none / coarse / fine | 主要指標裝置的精度。coarse = 手指,fine = 滑鼠/觸控筆 |
any-pointer | 同上 | 任一可用指標裝置。觸控筆電的 any-pointer 同時滿足 coarse 與 fine |
hover | none / hover | 主要指標能否停留產生 hover 狀態 |
any-hover | 同上 | 任一指標能否 hover |
/* 滑鼠:可以用小控制項、可以做 hover 提示 */
@media (hover: hover) and (pointer: fine) {
.btn { min-height: 28px; }
.btn:hover { background: #2a2a32; }
.tooltip-on-hover { display: block; }
}
/* 手指:命中區必須夠大,hover 提示要改成長按或常駐 */
@media (pointer: coarse) {
.btn { min-height: 48px; min-width: 48px; } /* Material 48dp */
.tooltip-on-hover { display: none; }
}
/* 混合裝置:兩種都要能用 → 以大命中區為準,但保留 hover 效果 */
@media (any-pointer: coarse) and (any-hover: hover) {
.btn { min-height: 44px; }
}命中區的兩個業界基準:Apple Human Interface Guidelines 建議 44×44pt,Material Design 建議 48×48dp。取 44px 是安全的下限,遊戲中的關鍵按鈕(攻擊、跳躍)建議再放大到 56–64px。
重點是命中區不隨遊戲 scale 縮小。畫布縮到 0.4 倍時,畫在 canvas 內的 48px 按鈕實際只剩 19px。解法有兩種:把互動控制項放在 DOM 層(不受 canvas transform 影響),或在 canvas 內另外維持一個以 CSS 像素計的觸控半徑:
// canvas 內的觸控判定:把門檻換算回設計座標
const TOUCH_RADIUS_CSS = 24 // 想要的實際半徑(CSS 像素)
const radiusInDesign = TOUCH_RADIUS_CSS / view.scale
const hit = dist(pointerDesign, button.center) < Math.max(button.r, radiusInDesign)方向:三種處理策略
(orientation: portrait) 比對的是視口長寬(height >= width),不是裝置的物理姿勢。這正是你要的語意。
策略 A:兩種方向都支援,UI 重排。 最好但成本最高。橫向把控制項移到左右兩側(拇指自然位置),直向移到底部。遊戲畫布的取景策略要能吃下兩種比例——expand 模式在這裡最順,因為它本來就是「安全框固定、視野彈性」。
@media (orientation: landscape) {
.controls { flex-direction: row; justify-content: space-between; align-items: flex-end; }
}
@media (orientation: portrait) {
.controls { flex-direction: column; align-items: center; }
}策略 B:只支援一種方向,另一種顯示提示。 最省事,體驗可接受。要用 CSS 而不是 JS,因為 CSS 沒有時序問題:
.rotate-hint { display: none; }
@media (orientation: portrait) and (pointer: coarse) {
.game-root { display: none; }
.rotate-hint { display: grid; place-items: center; }
}注意加上 (pointer: coarse):桌機使用者把視窗拉窄成直的,不該看到「請旋轉裝置」。
策略 C:軟性旋轉。 方向錯誤時,用 CSS 把整個 stage 旋轉 90 度:
@media (orientation: portrait) {
.stage {
width: 100vh; height: 100vw;
transform: translate(-50%, -50%) rotate(90deg);
position: absolute; top: 50%; left: 50%;
transform-origin: center center;
}
}這是 iPhone 上唯一能達成「強制橫向」效果的方法(見 全螢幕與沉浸模式),但代價很實在:輸入座標要跟著旋轉(getBoundingClientRect() 對旋轉元素回傳的是外接矩形,換算會錯)、所有 DOM 疊層都被一起轉、文字輸入與系統 UI 方向不一致。只在真的沒有其他選擇時用。
若採用策略 C,座標換算不能再用外接矩形,要改用完整的反矩陣:
const m = new DOMMatrixReadOnly(getComputedStyle(stage).transform)
const inv = m.inverse()
const p = inv.transformPoint(new DOMPoint(ev.clientX - originX, ev.clientY - originY))平板與桌機:畫面變大時該做什麼
畫面變大不代表要把所有東西等比放大——那只會得到「巨大的手機介面」。三個可行方向:
一、視野變大(expand)。 桌機看到更多戰場,這是最自然的用法。要注意公平性與鏡頭邊界(見 畫布縮放策略)。
二、UI 密度提高、資訊上浮。 手機上要點兩層選單才看得到的資訊(背包、技能樹、聊天),桌機直接常駐側欄。這是純粹的重排問題,用 media query 處理,與畫布縮放無關:
.layout { display: grid; grid-template-areas: "stage"; }
@media (min-width: 1024px) and (pointer: fine) {
.layout {
grid-template-columns: 1fr 320px;
grid-template-areas: "stage sidebar";
}
.sidebar { display: block; } /* 手機上是 display: none 的抽屜 */
}三、控制項改變形態。 手機的虛擬搖桿在桌機應該消失,換成 WASD + 滑鼠。這要由 pointer 查詢與實際收到的事件共同決定——最穩的做法是兩套都準備好,依最後一次輸入事件的 pointerType 動態切換:
let lastInput = matchMedia('(pointer: coarse)').matches ? 'touch' : 'mouse'
addEventListener('pointerdown', e => setInputMode(e.pointerType === 'touch' ? 'touch' : 'mouse'))
addEventListener('keydown', () => setInputMode('keyboard'))這樣接了藍牙鍵盤的平板、或用觸控的筆電,都會在使用者實際操作的當下切到對的控制介面,而不是被開場的猜測鎖死。
斷點該怎麼訂
滿版遊戲的斷點與內容網站不同——你不是在排文字欄,而是在決定「UI 佈局模式」。實務上三個模式就夠:
| 模式 | 條件 | 特徵 |
|---|---|---|
| compact | pointer: coarse 且短邊 < 480px | 控制項貼底或貼側,UI 疊在畫布上 |
| medium | 平板尺寸,或直向大螢幕 | 控制項有獨立空間,可顯示次要資訊 |
| expanded | min-width: 1024px 且 pointer: fine | 側欄常駐,畫布不被 UI 遮擋 |
用 min-width 而非 max-width(mobile-first,避免特異性打架),並且把模式寫成 CSS 自訂屬性讓 JS 也能讀:
:root { --ui-mode: 'compact'; }
@media (min-width: 768px) { :root { --ui-mode: 'medium'; } }
@media (min-width: 1024px) and (pointer: fine) { :root { --ui-mode: 'expanded'; } }摺疊機
env(viewport-segment-width X Y) 與 env(viewport-segment-height X Y) 提供各段視口的幾何,可用來避免把重要 UI 畫在鉸鏈上:
@media (horizontal-viewport-segments: 2) {
.hud-center { display: none; } /* 中央被鉸鏈切開,改用左右兩段 */
}支援度仍在推進中,且「跨鉸鏈的遊戲畫面該怎麼取景」沒有業界共識,目前建議是把它當漸進增強處理。
檢查清單
- 用
pointer/hover判斷輸入方式,不用寬度 - 觸控命中區 ≥ 44px,且不隨畫布 scale 縮小
- 「請旋轉裝置」提示有加
(pointer: coarse)條件 - hover 專屬資訊在觸控裝置有替代呈現(長按/常駐)
- 桌機不是單純放大手機 UI,有實質的資訊重排
- 實測:接鍵盤的平板、觸控筆電、桌機拉窄視窗
下一篇處理真正的沉浸模式與它在 iOS 上的天花板 → 全螢幕與沉浸模式