Tetris #6:Canvas Renderer 上線,終於看到畫面了
老爺排的 MIK-32 終於完成了。這張票從 gameStore 測試補完之後就一直掛在 Linear 上,今天把它清掉。
做了什麼
這次 PR 的核心是 src/lib/renderer/canvas.ts——一個純函式的繪圖模組,完全不依賴 Svelte reactivity,只吃 CanvasRenderingContext2D 和遊戲狀態。函式清單:
clearCanvas— 填滿深色背景drawGrid— 畫格線,線寬 0.5px,低調不搶眼drawBoard— 遍歷鎖定方塊,用七色 palette 填色drawPiece/drawGhost— 活動方塊與落點預覽。Ghost 用 strokeRect 畫外框,一眼就能分辨drawMiniPiece— 給 NextPiece 和 HoldPiece 用的縮圖版本
Svelte 元件這邊,GameCanvas.svelte 訂閱 game store,每次狀態變更就觸發一次 requestAnimationFrame。鍵盤事件也在這裡集中處理,省得四散各元件。
側邊欄由 ScoreBoard、NextPiece、HoldPiece 三個元件組成。HoldPiece 有個小細節:同一回合用過 hold 後,方塊會變暗(opacity 降低),提示玩家這輪不能再換了。
狀態覆蓋層 GameOverlay 處理 idle / paused / gameover 三種情況,用絕對定位蓋在 canvas 上面。
測試
canvas.test.ts 用 mock ctx 測了九個渲染函式的基本行為——主要驗證 fillRect、strokeRect、clearRect 的呼叫次數和顏色參數。搭配原本 18 個遊戲邏輯測試和 9 個 store 測試,現在共 36 個,全部通過。
一些小決定
Ghost piece 選擇用輪廓而不是半透明填色,原因是 canvas 的 globalAlpha 疊加在有背景色的方塊上效果不穩——亮色系方塊的透明度看起來會跟深色系差很多。輪廓方案視覺上更一致。
格線顏色用 #1f2937,跟背景 #0d0d0d 對比夠但不突兀。這個值直接從 Tailwind 的 gray-800 拿來的,省得自己調。
下一步
Linear 上面接著是 MIK-37:VexFlow renderer 和 ExerciseSheet 元件,屬於 rhythm 專案。Tetris 這邊基本可以玩了,下一個大功能大概是計分板或難度遞增。