feat: implement canvas-based rendering with geometric spaceship
- Replace DOM grid with HTML5 canvas (800x600) - Draw spaceship as geometric shapes (triangle + window + flames) - Green body, yellow window, orange flame trails - Smooth pixel-based rendering instead of DOM elements
This commit is contained in:
+61
-48
@@ -1,64 +1,77 @@
|
||||
import { useRef } from "react";
|
||||
import { useRef, useEffect } from "react";
|
||||
import type { PlayerPosition } from "../types";
|
||||
|
||||
const BOARD_WIDTH = 10;
|
||||
const BOARD_HEIGHT = 20;
|
||||
const CELL_SIZE = 40; // pixels
|
||||
const CANVAS_WIDTH = 800;
|
||||
const CANVAS_HEIGHT = 600;
|
||||
const SPACESHIP_Y = CANVAS_HEIGHT - 40;
|
||||
|
||||
export function useBoard() {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
|
||||
// Setup canvas on mount
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = CANVAS_WIDTH;
|
||||
canvas.height = CANVAS_HEIGHT;
|
||||
canvas.style.border = "1px solid #0f3460";
|
||||
canvas.style.backgroundColor = "#000";
|
||||
canvas.style.display = "block";
|
||||
|
||||
containerRef.current.innerHTML = "";
|
||||
containerRef.current.appendChild(canvas);
|
||||
canvasRef.current = canvas;
|
||||
}, []);
|
||||
|
||||
const renderBoard = (playerPosition: PlayerPosition) => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
container.innerHTML = "";
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
// Create board grid
|
||||
const board = document.createElement("div");
|
||||
board.className = "typing-board";
|
||||
board.style.display = "grid";
|
||||
board.style.gridTemplateColumns = `repeat(${BOARD_WIDTH}, ${CELL_SIZE}px)`;
|
||||
board.style.gap = "1px";
|
||||
board.style.backgroundColor = "#000";
|
||||
board.style.padding = "8px";
|
||||
board.style.borderRadius = "4px";
|
||||
board.style.width = "fit-content";
|
||||
// Clear canvas
|
||||
ctx.fillStyle = "#000";
|
||||
ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
|
||||
|
||||
// Create cells
|
||||
for (let row = 0; row < BOARD_HEIGHT; row++) {
|
||||
for (let col = 0; col < BOARD_WIDTH; col++) {
|
||||
const cell = document.createElement("div");
|
||||
cell.className = "typing-cell";
|
||||
cell.setAttribute("data-row", String(row));
|
||||
cell.setAttribute("data-col", String(col));
|
||||
cell.style.width = `${CELL_SIZE}px`;
|
||||
cell.style.height = `${CELL_SIZE}px`;
|
||||
cell.style.backgroundColor = "#1a1a2e";
|
||||
cell.style.border = "1px solid #0f3460";
|
||||
cell.style.display = "flex";
|
||||
cell.style.alignItems = "center";
|
||||
cell.style.justifyContent = "center";
|
||||
cell.style.fontSize = "12px";
|
||||
cell.style.fontFamily = "monospace";
|
||||
cell.style.color = "#00ff00";
|
||||
// Draw spaceship at player position
|
||||
const spaceshipX = (playerPosition.x / 10) * CANVAS_WIDTH + CANVAS_WIDTH / 20;
|
||||
const spaceshipY = SPACESHIP_Y;
|
||||
|
||||
// Highlight player row (bottom)
|
||||
if (row === BOARD_HEIGHT - 1) {
|
||||
cell.style.backgroundColor = "#0f3460";
|
||||
|
||||
// Render spaceship in player column
|
||||
if (col === playerPosition.x) {
|
||||
cell.innerHTML = "🚀";
|
||||
cell.style.fontSize = "24px";
|
||||
}
|
||||
}
|
||||
// Spaceship body (triangle pointing up)
|
||||
ctx.fillStyle = "#00ff00";
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(spaceshipX, spaceshipY - 15); // top point
|
||||
ctx.lineTo(spaceshipX - 10, spaceshipY + 10); // left bottom
|
||||
ctx.lineTo(spaceshipX + 10, spaceshipY + 10); // right bottom
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
board.appendChild(cell);
|
||||
}
|
||||
}
|
||||
// Spaceship window (circle in center)
|
||||
ctx.fillStyle = "#ffff00";
|
||||
ctx.beginPath();
|
||||
ctx.arc(spaceshipX, spaceshipY - 5, 3, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
container.appendChild(board);
|
||||
// Spaceship flames (small triangles at bottom)
|
||||
ctx.fillStyle = "#ff6600";
|
||||
// Left flame
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(spaceshipX - 5, spaceshipY + 10);
|
||||
ctx.lineTo(spaceshipX - 3, spaceshipY + 20);
|
||||
ctx.lineTo(spaceshipX - 8, spaceshipY + 15);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// Right flame
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(spaceshipX + 5, spaceshipY + 10);
|
||||
ctx.lineTo(spaceshipX + 3, spaceshipY + 20);
|
||||
ctx.lineTo(spaceshipX + 8, spaceshipY + 15);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user