遊戲出題卡頓時強制先畫出「出題中」遮罩,避免誤觸第一題 - #230
Conversation
按「開始挑戰」後,出題運算在慢速裝置上是同步且吃重的,會卡住主執行緒數秒。 過去雖然已經有 loading-indicator 遮罩,但設定 display:flex 後立刻同步呼叫 運算函式,瀏覽器來不及畫出遮罩就先被卡住;使用者在等待期間亂點的觸控事件 會被排進佇列,等運算結束、第一題畫面一出現就立刻誤觸到選項,導致答錯。 用雙重 requestAnimationFrame 確保遮罩真的畫出來後才開始算題,讓期間的誤觸 都被遮罩擋掉;同時停用開始/重玩按鈕防止連點,並把提示文字改成「出題中, 請小等一下」。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3WvXk9X1oZEZvxGDdhvns
There was a problem hiding this comment.
Code Review
This pull request updates the game UI to disable start and retry buttons during session generation and introduces a double requestAnimationFrame delay to ensure the loading indicator is fully rendered before heavy synchronous processing begins. The review feedback suggests adding defensive null checks for the loading elements and addresses a potential click-through issue where queued touch events could trigger answers immediately after the loading indicator is hidden, recommending a delayed hide via setTimeout.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| loadingIndicator.style.display = 'none'; | ||
| showGameView('play'); | ||
| renderQuestion(); |
There was a problem hiding this comment.
⚠️ 潛在的點擊穿透問題
在 generateGameSession 執行期間,若主執行緒因同步運算而卡頓,使用者在此期間點擊螢幕所產生的觸控事件會被瀏覽器排入佇列(Queue)。
由於隱藏遮罩(loadingIndicator.style.display = 'none')與顯示新畫面(showGameView('play')、renderQuestion())是在同一個微任務(Microtask)中同步執行的,當這個任務結束後,瀏覽器才會開始分發佇列中的觸控事件。此時遮罩已經被隱藏,導致這些佇列中的點擊事件會直接穿透並觸發在剛出現的第一題選項按鈕上,造成誤觸答錯。
💡 建議解決方案
建議在渲染完題目後,使用 setTimeout 延遲約 300ms 再隱藏遮罩。這樣可以確保所有在卡頓期間累積的點擊事件都會先被遮罩攔截並丟棄,之後遮罩才安全地消失。
| loadingIndicator.style.display = 'none'; | |
| showGameView('play'); | |
| renderQuestion(); | |
| showGameView('play'); | |
| renderQuestion(); | |
| setTimeout(() => { | |
| if (loadingIndicator) loadingIndicator.style.display = 'none'; | |
| }, 300); |
| loadingText.textContent = '出題中,請小等一下……'; | ||
| loadingIndicator.style.display = 'flex'; | ||
| if (startBtn) startBtn.disabled = true; | ||
| if (retryBtn) retryBtn.disabled = true; |
There was a problem hiding this comment.
為了提高程式碼的健壯性(Defensive Programming),建議在存取 loadingText 與 loadingIndicator 的屬性前先進行空值檢查(Null Check),避免在 DOM 元素不存在時引發 JavaScript 錯誤。
| loadingText.textContent = '出題中,請小等一下……'; | |
| loadingIndicator.style.display = 'flex'; | |
| if (startBtn) startBtn.disabled = true; | |
| if (retryBtn) retryBtn.disabled = true; | |
| if (loadingText) loadingText.textContent = '出題中,請小等一下……'; | |
| if (loadingIndicator) loadingIndicator.style.display = 'flex'; | |
| if (startBtn) startBtn.disabled = true; | |
| if (retryBtn) retryBtn.disabled = true; |
Review 摘要這個修法抓得很準。追進去看了 確認沒問題的地方
小建議(non-blocking)
測試專案目前沒有 DOM/UI 層級的自動化測試( 整體而言這是一個小而精準、註解說明「為什麼」而非「做什麼」的修法,沒有發現會擋 merge 的問題。 |
按「開始挑戰」後,出題運算在慢速裝置上是同步且吃重的,會卡住主執行緒數秒。
過去雖然已經有 loading-indicator 遮罩,但設定 display:flex 後立刻同步呼叫
運算函式,瀏覽器來不及畫出遮罩就先被卡住;使用者在等待期間亂點的觸控事件
會被排進佇列,等運算結束、第一題畫面一出現就立刻誤觸到選項,導致答錯。
用雙重 requestAnimationFrame 確保遮罩真的畫出來後才開始算題,讓期間的誤觸
都被遮罩擋掉;同時停用開始/重玩按鈕防止連點,並把提示文字改成「出題中,
請小等一下」。
Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01F3WvXk9X1oZEZvxGDdhvns