From b884884a043c2eb44ae086b413f52abb9a21ee04 Mon Sep 17 00:00:00 2001 From: syoul Date: Tue, 24 Mar 2026 00:28:31 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20heatmap=20overlay=20=E2=80=94=20masque?= =?UTF-8?q?=20les=20snapshots=20pendant=20zoom/pan,=20resync=20apr=C3=A8s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- src/components/HeatMap.tsx | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/components/HeatMap.tsx b/src/components/HeatMap.tsx index 9a24a03..3229410 100644 --- a/src/components/HeatMap.tsx +++ b/src/components/HeatMap.tsx @@ -63,7 +63,40 @@ export function HeatMap({ transactions }: HeatMapProps) { mapRef.current = map; heatRef.current = heat; + // Pendant zoom/pan : cache les overlays → le canvas live est visible directement. + // Après zoom/pan : resynchronise le snapshot sur le canvas redesssiné. + const hideOverlays = () => { + const prev = prevRef.current; + const next = nextRef.current; + if (prev) { prev.style.transition = 'none'; prev.style.opacity = '0'; } + if (next) { next.style.transition = 'none'; next.style.opacity = '0'; } + currentSrcRef.current = ''; + }; + + const syncAfterMove = () => { + const canvas = (heat as unknown as { _canvas?: HTMLCanvasElement })._canvas; + const next = nextRef.current; + if (!canvas || !next) return; + // Double RAF : leaflet.heat redessine en interne après l'événement + requestAnimationFrame(() => { + requestAnimationFrame(() => { + try { + const src = canvas.toDataURL(); + currentSrcRef.current = src; + next.src = src; + next.style.transition = 'none'; + next.style.opacity = '1'; + } catch { /* map torn down */ } + }); + }); + }; + + map.on('zoomstart movestart', hideOverlays); + map.on('zoomend moveend', syncAfterMove); + return () => { + map.off('zoomstart movestart', hideOverlays); + map.off('zoomend moveend', syncAfterMove); map.remove(); mapRef.current = null; heatRef.current = null;