前端的「最佳實踐」文章有個系統性問題:規範保證的行為、瀏覽器實際實作、以及部落格流傳的 hack,全部用同一種肯定語氣寫。結果是你照抄一段程式碼,在自己的裝置上正常,上線後在某個平台崩掉,而原文完全沒提那個平台不支援。

這一篇把本專欄的主要主張逐條分級。

分級定義:

  • 【規範保證】:CSS/HTML/Web API 規範明文定義的行為,實作若不符即為 bug。
  • 【Baseline 可用】:規範定義且四大引擎皆已實作,可直接使用。
  • 【部分支援】:主流引擎中至少一個未實作或行為不同,必須 feature-detect + 降級。
  • 【實務共識】:規範沒規定,但業界做法一致且有可驗證的理由。
  • 【平台相依/未文件化】:依賴特定瀏覽器的實作細節,可能隨版本改變。

一、視口與單位

主張分級依據與但書
vh 等於 lvh,即工具列收起時的高度規範保證CSS Values 4;這正是 100vh 在行動裝置「超出」可視區的原因
svhlvhdvh 可直接使用Baseline 可用Chrome/Edge 108+、Firefox 101+、Safari 15.4+;2025-06 起 Baseline Widely available
dvh 的更新會被節流,不保證 60fps規範保證CSS Values 4 明示 UA 可節流;web.dev 補充部分瀏覽器會依手勢完全 debounce
position: fixed; inset: 0100dvh 更適合遊戲主容器實務共識推論自上一條:dvh 的階梯式跳動會傳導成 canvas 重建。規範未規定何者「更貼合」
env(safe-area-inset-*) 需要 viewport-fit=cover 才非零規範保證CSS Env 1 / WebKit 官方說明;未設 cover 時瀏覽器已自動縮排,故 inset 為 0
max(基準值, env(...)) 是安全區的正確寫法實務共識WebKit 官方部落格〈Designing Websites for iPhone X〉即以此為範例
interactive-widget=resizes-content 可讓鍵盤縮小佈局視口部分支援Chrome 108+、Firefox 132+;Safari 尚未支援(見於 STP 功能旗標),Chrome on iOS 亦不支援(WebKit 核心)
user-scalable=no 能阻止縮放錯誤iOS Safari 自 10 起忽略此設定;且違反 WCAG 2.1。正確工具是 touch-action

二、Canvas 與縮放

主張分級依據與但書
設定 canvas.width/height 會清空 bitmap 並重設 drawing state規範保證HTML 規範明定;設成相同值亦然,故需 if 守衛
backing store 要乘 devicePixelRatio 才不模糊規範保證由 CSS 像素與裝置像素的定義推導;MDN devicePixelRatio 頁的正式範例
devicePixelRatio 受頁面縮放影響、不受捏合縮放影響規範保證MDN 明載
letterbox/cover/expand 三選一是幾何必然規範保證純數學:長寬比不等時,等比縮放無法同時滿足「填滿」與「不裁切」
object-fit: contain/cover 等同 letterbox/cover規範保證CSS Images 3 的定義
CSS transform 縮放比 resize backing store 快實務共識MDN canvas 最佳化明確建議,並註明「只適合放大,不適合縮小」;實際差距依 GPU 而異
device-pixel-content-box 給出整數實體像素部分支援規範定義明確,但屬後加選項;不支援的瀏覽器對未知 box 值丟 TypeError,必須 try/catch
render scale 0.75 的畫質下降多數人察覺不到實務共識主機/PC 遊戲動態解析度的普遍經驗;沒有針對網頁的公開受試者研究,數字應視為起點而非結論
Safari 的 canvas 記憶體上限會導致畫面消失平台相依/未文件化只有社群實測值,Apple 未公開單一 canvas 面積與行程總量的上限,且隨版本變動

三、事件與重算

主張分級依據與但書
ResizeObserver 回呼在版面計算後、繪製前執行規範保證Resize Observer 規範定義的 observation loop 時機
在回呼內改變被觀察元素尺寸會使通知被丟棄規範保證規範定義的深度限制與 error event;這正是 undelivered notifications 警告的來源
orientationchangeresize 讀到的尺寸可能是舊值部分支援/實作差異規範未規定觸發時機與版面的相對順序,各瀏覽器不一。這是「要轉兩次才對」的成因
(orientation: portrait) 比對視口而非裝置姿勢規範保證Media Queries 4 定義為 height >= width
visualViewport 可用於推算鍵盤高度Baseline 可用(API)/實務共識(推算法)API 自 2021-08 Baseline;但「innerHeight − vv.height − vv.offsetTop 等於鍵盤高度」是推論,非規範保證,> 100px 門檻更是經驗值
讀取 offsetHeightgetBoundingClientRect() 會觸發同步版面計算實務共識所有主流引擎皆如此,但屬實作行為而非規範要求

四、全螢幕與沉浸

主張分級依據與但書
requestFullscreen() 需要 transient user activation規範保證Fullscreen API 規範
iPhone Safari 的 Fullscreen API 不可依賴部分支援caniuse 標記 iOS Safari 12–最新版皆為 partial;macOS Safari 自 16.4 完整支援。部分版本需開啟功能旗標
screen.orientation.lock() 需先進入全螢幕、且只在行動裝置可用部分支援MDN 明載「通常只在行動裝置且全螢幕時啟用」,並標為 Limited availability;Safari 不支援
PWA standalone 是 iPhone 上取得無工具列滿版的可行方案實務共識Apple 的 apple-mobile-web-app-capable 有官方文件,但「這是 iPhone 上唯一可靠方案」是歸納,非 Apple 的表述
manifestorientation 欄位 iOS 不遵守平台相依廣泛實測結果,Apple 未文件化其忽略行為
touch-action: none 會傷害無障礙規範保證(警告)MDN 明確指出違反 WCAG 2.0 SC 1.4.4;manipulation 是較安全的選擇
Wake Lock 切到背景會自動釋放規範保證Screen Wake Lock 規範;需安全上下文與頁面可見

五、本專欄的設計選擇(屬於主張,不是事實)

以下是我在寫作時做的取捨,讀者完全可以有不同結論:

  • 「遊戲邏輯不該知道螢幕尺寸」 是一條架構紀律,不是技術限制。有些遊戲(無限捲動、程序生成)確實需要依螢幕尺寸產生內容,那時該做的是把「視野尺寸」當成明確的輸入參數傳進去,而不是讓遊戲各處直接讀 window.innerWidth
  • 推薦 expand 作為現代手遊的預設策略,前提是你的遊戲不受視野差異影響公平性。競技類請退回 letterbox 或 cover + 嚴格安全框。
  • DPR 夾在 2–2.5 的建議是效能與畫質的折衷點,數字來自像素量的平方關係與常見裝置分布,不是實驗結論。畫質優先的產品可以放寬到 3。
  • 建議把文字放 DOM 層 在文字量大時成立;若你的遊戲只有幾個數字,canvas 內畫反而省掉一整層合成。

六、延伸資源(皆為一手來源)

規範與參考

官方說明文章

支援度查詢

怎麼自己驗證一條主張

  1. 先查 MDN 的 Browser compatibility 表——它由 browser-compat-data 自動生成,比部落格可靠。
  2. 交叉查 caniuse 的 Known issues 分頁——MDN 只記錄「有無支援」,caniuse 才會記錄「支援但有 bug」。
  3. 看規範原文(W3C/WHATWG)確認是「規範要求」還是「實作巧合」。這一步能過濾掉大部分會在下個版本消失的 hack。
  4. 實機驗證——工具列行為、safe-area、鍵盤、DPR 上限這四類,DevTools 的裝置模擬都不完整。

回到總覽 → 全響應式最佳實踐