import { useEffect, useRef, type PointerEvent } from "react"; import { ZUSTAENDE, type Zelle } from "../modell"; // Zustandsfarben als RGB, einmal vorberechnet const FARBEN_RGB = ZUSTAENDE.map(({ farbe }) => [1, 3, 5].map((i) => parseInt(farbe.slice(i, i + 2), 16))); interface Props { breite: number; hoehe: number; zustand: Uint8Array; /** Erhöhen, um neu zu zeichnen, wenn das Gitter direkt verändert wurde */ version?: number; /** Wird beim Klicken und Ziehen mit der Zelle unter dem Zeiger aufgerufen */ onZelle?: (zelle: Zelle) => void; beschriftung: string; } export default function Gitter({ breite, hoehe, zustand, version, onZelle, beschriftung }: Props) { const canvasRef = useRef(null); const maltRef = useRef(false); useEffect(() => { const ctx = canvasRef.current?.getContext("2d"); if (!ctx) return; const bild = ctx.createImageData(breite, hoehe); for (let i = 0; i < zustand.length; i++) { const [r, g, b] = FARBEN_RGB[zustand[i]]; bild.data[4 * i] = r; bild.data[4 * i + 1] = g; bild.data[4 * i + 2] = b; bild.data[4 * i + 3] = 255; } ctx.putImageData(bild, 0, 0); }, [breite, hoehe, zustand, version]); function zelleUnterZeiger(e: PointerEvent): Zelle { const rechteck = e.currentTarget.getBoundingClientRect(); return [ Math.floor(((e.clientY - rechteck.top) / rechteck.height) * hoehe), Math.floor(((e.clientX - rechteck.left) / rechteck.width) * breite), ]; } return ( { if (!onZelle) return; maltRef.current = true; e.currentTarget.setPointerCapture(e.pointerId); onZelle(zelleUnterZeiger(e)); }} onPointerMove={(e) => { if (onZelle && maltRef.current) onZelle(zelleUnterZeiger(e)); }} onPointerUp={() => (maltRef.current = false)} onPointerCancel={() => (maltRef.current = false)} /> ); }