From d255c1cce133391b418a568e45b688be8a65a27a Mon Sep 17 00:00:00 2001 From: iamdoubz <> Date: Sat, 20 Jun 2026 05:11:03 -0500 Subject: [PATCH] Details lesson loads your outline drawing as the canvas's base image, but that saved image was composited on an opaque white background, so it painted over the faint guide and the line-by-line video underneath --- src/components/LessonRunner.tsx | 4 ++-- src/components/ShadingRunner.tsx | 4 ++-- src/components/TraceRunner.tsx | 4 ++-- src/components/drawingPersistence.ts | 10 +++++++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/components/LessonRunner.tsx b/src/components/LessonRunner.tsx index fb3fa5f..6579ba4 100644 --- a/src/components/LessonRunner.tsx +++ b/src/components/LessonRunner.tsx @@ -159,7 +159,7 @@ export default function LessonRunner({ if (!loggedIn) return; const c = canvasRef.current; if (!c || !hasContent()) return; - saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c)); + saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c, 800, false)); } function scheduleSave() { if (!loggedIn) return; @@ -198,7 +198,7 @@ export default function LessonRunner({ redraw(); force((n) => n + 1); const c = canvasRef.current; - if (loggedIn && c) saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c)); + if (loggedIn && c) saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c, 800, false)); } function undo() { strokesRef.current.pop(); diff --git a/src/components/ShadingRunner.tsx b/src/components/ShadingRunner.tsx index d01cf8b..3df5f2c 100644 --- a/src/components/ShadingRunner.tsx +++ b/src/components/ShadingRunner.tsx @@ -125,7 +125,7 @@ export default function ShadingRunner({ if (!loggedIn) return; const c = canvasRef.current; if (!c || !hasContent()) return; - saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c)); + saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c, 800, false)); } function scheduleSave() { if (!loggedIn) return; @@ -158,7 +158,7 @@ export default function ShadingRunner({ redraw(); force((n) => n + 1); const c = canvasRef.current; - if (loggedIn && c) saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c)); + if (loggedIn && c) saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c, 800, false)); } function undo() { strokesRef.current.pop(); redraw(); force((n) => n + 1); scheduleSave(); } diff --git a/src/components/TraceRunner.tsx b/src/components/TraceRunner.tsx index e150838..fc81505 100644 --- a/src/components/TraceRunner.tsx +++ b/src/components/TraceRunner.tsx @@ -86,14 +86,14 @@ export default function TraceRunner({ meta, steps, loggedIn, username, completed useEffect(() => { setPlaying(true); setPlayKey((k) => k + 1); }, [idx]); function hasContent() { return strokesRef.current.length > 0 || !!baseImgRef.current; } - function flushSave() { if (!loggedIn) return; const c = canvasRef.current; if (!c || !hasContent()) return; saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c)); } + function flushSave() { if (!loggedIn) return; const c = canvasRef.current; if (!c || !hasContent()) return; saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c, 800, false)); } function scheduleSave() { if (!loggedIn) return; clearTimeout(saveTimer.current); saveTimer.current = setTimeout(flushSave, 1200); } function pe(e: React.PointerEvent): Point { const r = canvasRef.current!.getBoundingClientRect(); return { x: (e.clientX - r.left) / r.width, y: (e.clientY - r.top) / r.height }; } function onDown(e: React.PointerEvent) { if (finished) return; (e.target as Element).setPointerCapture?.(e.pointerId); const s: Stroke = { color, size, points: [pe(e)] }; drawingRef.current = s; strokesRef.current.push(s); } function onMove(e: React.PointerEvent) { const s = drawingRef.current; if (!s) return; s.points.push(pe(e)); redraw(); } function onUp() { drawingRef.current = null; force((n) => n + 1); scheduleSave(); } - function clearCanvas() { strokesRef.current = []; baseImgRef.current = null; redraw(); force((n) => n + 1); const c = canvasRef.current; if (loggedIn && c) saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c)); } + function clearCanvas() { strokesRef.current = []; baseImgRef.current = null; redraw(); force((n) => n + 1); const c = canvasRef.current; if (loggedIn && c) saveProgressDrawing(meta.level, meta.sublevel, exportCanvasToDataURL(c, 800, false)); } function undo() { strokesRef.current.pop(); redraw(); force((n) => n + 1); scheduleSave(); } async function saveStep(stepN: number, badgeKey?: string) { diff --git a/src/components/drawingPersistence.ts b/src/components/drawingPersistence.ts index 3d2fb4e..082399a 100644 --- a/src/components/drawingPersistence.ts +++ b/src/components/drawingPersistence.ts @@ -42,7 +42,7 @@ export async function saveFinalDrawing( } /** Composite the (transparent) canvas onto white and downscale, returning a PNG data URL. */ -export function exportCanvasToDataURL(canvas: HTMLCanvasElement, maxW = 800): string { +export function exportCanvasToDataURL(canvas: HTMLCanvasElement, maxW = 800, white = true): string { const scale = Math.min(1, maxW / canvas.width); const w = Math.round(canvas.width * scale); const h = Math.round(canvas.height * scale); @@ -50,8 +50,12 @@ export function exportCanvasToDataURL(canvas: HTMLCanvasElement, maxW = 800): st out.width = w; out.height = h; const ctx = out.getContext("2d")!; - ctx.fillStyle = "#ffffff"; - ctx.fillRect(0, 0, w, h); + // White background for saved/gallery images; transparent for in-progress canvases that are + // restored as a base layer (so the faint guide and the line-by-line video still show through). + if (white) { + ctx.fillStyle = "#ffffff"; + ctx.fillRect(0, 0, w, h); + } ctx.drawImage(canvas, 0, 0, w, h); return out.toDataURL("image/png"); }