Files
website/src/lib/components/games/SnakeCanvas.svelte
T

70 lines
1.9 KiB
Svelte

<script lang="ts">
import { boardSize, type Point } from '#lib/games/snake';
import { themeState } from '#lib/theme/theme.svelte';
let { snake, food, size }: { snake: Point[]; food: Point | null; size: number } = $props();
const emptyCellOpacity = 0.06;
let canvasElement = $state<HTMLCanvasElement>();
let pixelRatio = $state(1);
const canvasSize = $derived(Math.round(size * pixelRatio));
function themeColor(name: string): string {
return getComputedStyle(canvasElement!).getPropertyValue(name).trim();
}
function drawBoard(context: CanvasRenderingContext2D) {
const gap = Math.max(1, Math.round(pixelRatio));
const cellSpan = (canvasSize - gap) / boardSize;
const fillCell = (point: Point) => {
const left = gap + Math.round(point.x * cellSpan);
const top = gap + Math.round(point.y * cellSpan);
const right = Math.round((point.x + 1) * cellSpan);
const bottom = Math.round((point.y + 1) * cellSpan);
context.fillRect(left, top, right - left, bottom - top);
};
context.clearRect(0, 0, canvasSize, canvasSize);
context.globalAlpha = emptyCellOpacity;
context.fillStyle = themeColor('--foreground');
for (let y = 0; y < boardSize; y++) {
for (let x = 0; x < boardSize; x++) fillCell({ x, y });
}
context.globalAlpha = 1;
if (food) {
context.fillStyle = themeColor('--red');
fillCell(food);
}
context.fillStyle = themeColor('--green');
snake.slice(1).forEach(fillCell);
context.fillStyle = themeColor('--accent');
if (snake[0]) fillCell(snake[0]);
}
$effect(() => {
size;
pixelRatio = window.devicePixelRatio || 1;
});
$effect(() => {
themeState.currentId;
const context = canvasElement?.getContext('2d');
if (context && canvasSize > 0) drawBoard(context);
});
</script>
<canvas bind:this={canvasElement} width={canvasSize} height={canvasSize} style="width: {size}px; height: {size}px"></canvas>
<style>
canvas {
display: block;
}
</style>