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
This commit is contained in:
1 parent
4ab06366a6
commit
d255c1cce1
4 files changed
+13
-9
No files matched your search
@@ -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();
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user