diff --git a/src/components/HeatMap.tsx b/src/components/HeatMap.tsx index ef12da7..64148f7 100644 --- a/src/components/HeatMap.tsx +++ b/src/components/HeatMap.tsx @@ -91,26 +91,28 @@ export function HeatMap({ transactions }: HeatMapProps) { return; } - // 2. Freeze current frame in the overlay + // 1. Cancel any running overlay transition instantly (flush only the overlay) + overlay.style.transition = 'none'; + overlay.style.opacity = '0'; + void overlay.offsetWidth; // flush overlay only — avoids double-paint with canvas + + // 2. Capture current frame (canvas still visible at opacity 1) try { overlay.src = canvas.toDataURL(); } catch { - // canvas tainted (shouldn't happen with heatmap-only canvas) update(); return; } - // 1. Reset transitions instantly — force reflow to flush any running transition + // 3. In one paint batch: hide canvas + show overlay (Frame A moves from canvas to overlay) canvas.style.transition = 'none'; - overlay.style.transition = 'none'; - void canvas.offsetWidth; // force reflow canvas.style.opacity = '0'; overlay.style.opacity = '1'; - // 3. Update heatmap (invisible: canvas still at opacity 0) + // 4. Update heatmap (invisible: canvas at opacity 0) update(); - // 4. Simultaneous crossfade: overlay fades out, canvas fades in + // 5. Simultaneous crossfade: overlay fades out, canvas fades in const raf = requestAnimationFrame(() => { requestAnimationFrame(() => { const DURATION = '0.5s ease-in-out';