67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
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<HTMLCanvasElement>(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<HTMLCanvasElement>): 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 (
|
|
<canvas
|
|
ref={canvasRef}
|
|
className={onZelle ? "gitter gitter--interaktiv" : "gitter"}
|
|
width={breite}
|
|
height={hoehe}
|
|
style={{ aspectRatio: `${breite} / ${hoehe}` }}
|
|
role="img"
|
|
aria-label={beschriftung}
|
|
onPointerDown={(e) => {
|
|
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)}
|
|
/>
|
|
);
|
|
}
|