From a961814ecb9a56b04586216608b6084d959b76af Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 20:38:52 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20Web-Canvas=20mit=20ganzzahligen=20Pixelm?= =?UTF-8?q?a=C3=9Fen,=20Fills=20verschwanden=20bei=20gebrochener=20Breite?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Das canvas-Attribut schneidet Nachkommastellen ab, getImageData und floodFillPixels rechneten aber mit der übergebenen (gebrochenen) Breite. Bei z. B. screenWidth * 0.44 = 184,8 stimmte die Zeilenadressierung nicht mehr und Fills verschwanden still (auf dem Ergebnis-Screen und auf Geräten mit gebrochener Fensterbreite auch in der Zeichenphase). width/height werden jetzt am Komponentenanfang abgerundet. Regressionstest prüft ganzzahlige Canvas-Maße und getImageData-Argumente; im Browser bei 420 px Viewport verifiziert (vorher keine Fills im Ergebnis, jetzt sichtbar). Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_017fxbsNALrmwMj7LGMDPLw2 --- CHANGELOG.md | 1 + components/DrawingCanvas.web.tsx | 11 ++++++++-- .../__tests__/DrawingCanvas.web.test.tsx | 20 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0daa457..d2a34b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Web: Füllungen verschwanden (u. a. auf dem Ergebnis-Screen), wenn die Canvas-Breite nicht ganzzahlig war (z. B. `screenWidth * 0.44` = 184,8 px). Die Pixelmaße der Web-Zeichenfläche werden jetzt abgerundet, damit Canvas-Attribut, `getImageData` und Flood-Fill dieselbe Zeilenbreite verwenden - „Von Level 1 neu starten" setzt den Galerie-Speicherstatus zurück (vorher blieb „In Galerie speichern" ggf. gesperrt) - Ergebnis-Feedback wechselte bei jedem Re-Render, weil die rotierende Nachricht im Render gewählt wurde — jetzt einmal pro Bewertung; DE/EN-Texte für 1–2 Sterne laufen nicht mehr auseinander diff --git a/components/DrawingCanvas.web.tsx b/components/DrawingCanvas.web.tsx index b9e426b..c7149e6 100644 --- a/components/DrawingCanvas.web.tsx +++ b/components/DrawingCanvas.web.tsx @@ -22,7 +22,7 @@ interface Props { export default function DrawingCanvas({ width: propWidth, - height = 400, + height: heightProp = 400, strokeColor = '#000000', strokeWidth = 3, tool = 'brush', @@ -30,7 +30,14 @@ export default function DrawingCanvas({ onDrawingChange, }: Props) { const { width: windowWidth } = useWindowDimensions(); - const width = propWidth ?? (windowWidth > 0 ? windowWidth - 48 : DEFAULT_CANVAS_WIDTH); + // Canvas-Pixelmaße müssen ganzzahlig sein: Das canvas-Attribut schneidet Nachkommastellen ab, + // getImageData/floodFillPixels rechnen aber mit der übergebenen Breite — bei gebrochenen Maßen + // (z. B. screenWidth * 0.44 = 184,8) stimmt die Zeilenadressierung nicht mehr und Fills + // verschwinden still. + const width = Math.floor( + propWidth ?? (windowWidth > 0 ? windowWidth - 48 : DEFAULT_CANVAS_WIDTH), + ); + const height = Math.floor(heightProp); const [currentPath, setCurrentPath] = useState<{ x: number; y: number }[]>([]); const isDrawingRef = useRef(false); diff --git a/components/__tests__/DrawingCanvas.web.test.tsx b/components/__tests__/DrawingCanvas.web.test.tsx index f737611..6439ac7 100644 --- a/components/__tests__/DrawingCanvas.web.test.tsx +++ b/components/__tests__/DrawingCanvas.web.test.tsx @@ -117,4 +117,24 @@ describe('DrawingCanvas.web — Live-Strich nutzt Snapshot statt Komplett-Redraw expect(newPaths).toHaveLength(1); expect(newPaths[0].points.length).toBeGreaterThan(1); }); + + describe('gebrochene Canvas-Maße', () => { + it('verwendet ganzzahlige Maße für Canvas und Pixelzugriff (Fills verschwanden sonst)', () => { + const fill: DrawingPath = { + points: [{ x: 5, y: 5 }], + color: '#FF0000', + strokeWidth: 0, + type: 'fill', + }; + const { container } = render(); + const canvas = container.querySelector('canvas') as HTMLCanvasElement; + + expect(canvas.width).toBe(184); + expect(canvas.height).toBe(100); + expect(fakeCtx.getImageData).toHaveBeenCalledWith(0, 0, 184, 100); + // Der Puffer passt zur übergebenen Breite → Flood-Fill kann korrekt adressieren + const [, , w, h] = fakeCtx.getImageData.mock.calls[0]; + expect(Number.isInteger(w) && Number.isInteger(h)).toBe(true); + }); + }); });