diff --git a/lidar_pipeline/index.py b/lidar_pipeline/index.py index 198729f..4003f4a 100644 --- a/lidar_pipeline/index.py +++ b/lidar_pipeline/index.py @@ -1633,9 +1633,10 @@ input[type="range"]::-moz-range-thumb { /* Layout et peinture des tuiles hors écran skippés par le navigateur */ content-visibility: auto; } -/* Uniquement pendant l'animation de zoom (classe posée par app.js) : - scaling sur le GPU, puis libération pour ne pas immobiliser de mémoire. */ -body.zooming .ltile { will-change: transform; } +/* Uniquement pendant l'animation de zoom (classe posée par app.js) : le pane + des tuiles porte le scaling — promotion GPU de lui seul (et non des ~1000 + div de tuiles), puis libération pour ne pas immobiliser de mémoire. */ +body.zooming .leaflet-lidarTiles-pane { will-change: transform; } .ltile img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: fill; display: block; @@ -1912,10 +1913,23 @@ const map = L.map('viewport', { zoomControl: false, attributionControl: false, minZoom: 4, - maxZoom: 19 + maxZoom: 19, + // Les marqueurs ne doivent pas glisser individuellement pendant le zoom : + // le pane lidarTiles est mis à l'échelle d'un bloc (voir zoomanim ci-dessous), + // le glissement propre des icônes s'ajouterait au scaling (double mouvement). + // Seul effet collatéral : le marqueur GPS saute à la fin d'un zoom. + markerZoomAnimation: false }); -// Pane dédié au dessin de zones : les tuiles LiDAR sont des marqueurs -// (pane 600) et les vecteurs par défaut vivent en dessous (pane 400) ; +// Pane dédié aux tuiles LiDAR (marqueurs) : pendant l'animation de zoom il +// reçoit la même mise à l'échelle que le fond de carte (translate + scale, +// transition CSS partagée via la classe leaflet-zoom-animated) — voir le +// gestionnaire zoomanim plus bas. La classe .leaflet-zoom-animated fournit +// aussi transform-origin: 0 0 (feuille de style Leaflet). +const lidarTilesPane = map.createPane('lidarTiles'); +lidarTilesPane.style.zIndex = 600; +lidarTilesPane.classList.add('leaflet-zoom-animated'); +// Pane dédié au dessin de zones : les tuiles LiDAR vivent en dessous (pane +// lidarTiles, 600) et les vecteurs par défaut vivent en dessous (pane 400) ; // on place rectangle et cellules fantômes au-dessus (620), sans capter // les clics (pointer-events: none → la carte reste manipulable). map.createPane('genDraw'); @@ -2022,7 +2036,8 @@ function registerTile(t) { const mk = L.marker(L.latLng(center[0], center[1]), { icon: icon, interactive: true, - keyboard: false + keyboard: false, + pane: 'lidarTiles' }).addTo(map); mk.on('click', () => { if (exportMode) { toggleExportSel(t); return; } @@ -2736,8 +2751,24 @@ gpsBtn.addEventListener('click', () => { } }); // will-change pendant l'animation de zoom uniquement : le scaling passe sur -// le GPU, et les couches sont libérées juste après (mémoire vidéo). +// le GPU, et la couche est libérée juste après (mémoire vidéo). let zoomGpuTimer = null; +// Animation de zoom des tuiles calée sur le fond de carte : les marqueurs +// Leaflet glissent (transition de position) sans changer de taille — les +// tuiles LiDAR « nageaient » donc sur le fond pendant l'animation puis +// sautaient à la fin. On applique au pane entier la mise à l'échelle que +// Leaflet applique aux couches de dalles : translate + scale autour de +// l'origine de la vue cible. La cible coïncide exactement avec la position +// que Marker._animateZoom donnerait à chaque icône (project(latlng, z1) − +// _getNewPixelOrigin(center, z1)) : le swap à la fin du zoom est sans saut. +map.on('zoomanim', (e) => { + const s = map.getZoomScale(e.zoom, map.getZoom()); + const po0 = map.getPixelOrigin(); + const po1 = map._getNewPixelOrigin(e.center, e.zoom); + lidarTilesPane.style.transform = + 'translate3d(' + (s * po0.x - po1.x) + 'px,' + (s * po0.y - po1.y) + 'px,0) scale(' + s + ')'; +}); +map.on('zoomend', () => { lidarTilesPane.style.transform = ''; }); map.on('zoomstart', () => { clearTimeout(zoomGpuTimer); document.body.classList.add('zooming');