跳轉到

D-20260811-08|玩家動態偏好與單一有效狀態

背景與驅動力

設定 canon 原先宣告 reducedMotion: boolean,實作卻只有 CSS 的作業系統 prefers-reduced-motion media query。玩家不能在遊戲內主動要求減少動態效果,CSS 特例、 3D 展示自轉與未來裝飾性動態也沒有共用的有效狀態。專案仍在開發期,因此直接修訂未發布 資料模型,不保留舊欄位或遷移相容層。

考慮過的選項

  • 各頁自行合併玩家設定與 media query:會重複 listener 與語意,未來 consumer 容易分歧,棄。
  • 只把結果投影到 DOM,非 CSS consumer 每幀讀 attribute:改動較小,但把執行期邏輯耦合到 presentation DOM,棄。
  • 建立集中式 motion preference service,讓 DOM、CSS 與 3D consumer 共用有效狀態(採納)。

決定

  • DisplaySettings 使用 motionPreference: 'system' | 'reduce'。default 與 sanitizer fallback 均為 system;不接受舊 reducedMotion、alias 或 migration。system 跟隨 OS,reduce 只能 增加保護,不能覆蓋 OS 的 reduce 偏好。
  • 唯一 effective reduced motion 為「玩家選 reduce,或 OS media query 為 reduce」。runtime 只建立一個 MediaQueryList listener,OS 偏好變更立即重算,生命週期結束時解除 listener。
  • 靜態與 SSG HTML 初始使用 data-motion="pending"。pending 與 reduce 都套用 motion safety; 只有 browser 已載入設定且確認有效狀態為 full 時才改成 data-motion="full"。沒有 DOM、沒有 matchMedia 或 JavaScript 啟動失敗時維持 fail-safe pending。
  • CSS safety 同時守住 OS media query 與 root data-motion。通用動畫/轉場近乎立即、迭代一次、 smooth scroll 關閉;animation: nonetransform: none 等特例也要有 attribute 等效路徑。 原本只在 no-preference 啟用的裝飾動畫改由 data-motion="full" 守門,pending 不得播放。
  • Home、Garage 與 Result 的 vehicle-showcase descriptor 仍只表達「此場景允許自轉」。Viewport render loop 讀共享 effective signal;reduce 時停止增加內容群組角度,full 後從原角度續轉, 不重建 scene、vehicle、camera 或 renderer。
  • motion preference 只控制非必要 presentation。不得停止比賽物理、Viewport render loop、 Editor、必要比賽鏡頭追蹤、HUD、gamepad polling、network 或 spectator 更新。 effectsQuality 仍是效能/畫質軸,與動態舒適度正交。
  • 設定頁提供「跟隨系統/減少動態」兩段選項與說明,未登入可用且立即持久化;display reset、 本機備份 round-trip、四語 i18n、鍵盤與螢幕閱讀器標示使用既有設定契約。

後果與影響

所有現有及未來非必要動態有單一可測試的 runtime source,SSR 與 IndexedDB 載入空窗不會先 播放再突然停止。Viewport 仍持續 render,因此控制項、資產載入與場景呈現不中斷;只略過展示 內容群組的角度增量。代價是新增一個 root-scope service,並需逐一把現有 reduced-motion CSS 特例接到 data-motion safety,但不增加網路、共識或 gameplay 狀態。