From 0df93c2f006742a688c2ad9c5506859b8794d7e1 Mon Sep 17 00:00:00 2001 From: Antoine Jacquin Date: Sun, 27 Sep 2026 19:54:00 +0200 Subject: [PATCH] =?UTF-8?q?Borner=20la=20barre=20de=20comparaison=20hors?= =?UTF-8?q?=20du=20panneau=20et=20des=20outils,=20poign=C3=A9e=20au-dessus?= =?UTF-8?q?=20du=20volet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- lidar_pipeline/tests/test_mapserve.py | 14 +++++++++++ lidar_pipeline/web/map.css | 4 ++- lidar_pipeline/web/map.js | 35 +++++++++++++++++++++------ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/lidar_pipeline/tests/test_mapserve.py b/lidar_pipeline/tests/test_mapserve.py index 5553fa7..bb437ba 100644 --- a/lidar_pipeline/tests/test_mapserve.py +++ b/lidar_pipeline/tests/test_mapserve.py @@ -525,6 +525,20 @@ def test_ui_compare_mode_replaces_both(): assert "'both'" in parse and "'relief'" in parse assert "#compareBar" in _MAP_CSS and "touch-action: none" in _MAP_CSS + # La poignée reste hors du panneau et de la colonne d'outils : séparation + # affichée bornée (barre et découpe ensemble), y compris en glissant, et + # recalculée quand le panneau change de largeur. + assert "function compareBounds(" in _MAP_JS + bounds = _MAP_JS[_MAP_JS.index("function compareBounds("):_MAP_JS.index("function applyCompareClip(")] + assert "el('panel').getBoundingClientRect()" in bounds and "el('tools').getBoundingClientRect()" in bounds + clip = _MAP_JS[_MAP_JS.index("function applyCompareClip("):_MAP_JS.index("(function compareDrag(")] + assert "compareBounds()" in clip + drag = _MAP_JS[_MAP_JS.index("(function compareDrag("):] + assert "compareBounds()" in drag[:drag.index("})();")] + assert "document.addEventListener('panel:tab', applyCompareClip)" in _MAP_JS + # Poignée centrée sur la carte visible au-dessus du volet (téléphone) + assert "top: calc((100% - var(--sheet-h)) / 2)" in _MAP_CSS + def test_ui_applies_server_defaults(): """L'interface part des défauts du serveur et sait les (re)définir.""" diff --git a/lidar_pipeline/web/map.css b/lidar_pipeline/web/map.css index ec7efbb..9885689 100644 --- a/lidar_pipeline/web/map.css +++ b/lidar_pipeline/web/map.css @@ -60,7 +60,9 @@ html, body { height: 100%; overflow: hidden; background: var(--bg); color: var(- /* Mode Comparer : barre glissante qui sépare les deux couches. */ #compareBar { position: fixed; top: 0; bottom: 0; width: 4px; margin-left: -2px; z-index: var(--z-map-ui); background: var(--surface); box-shadow: var(--shadow-sm); cursor: ew-resize; touch-action: none; } -#compareBar span { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); +/* Poignée au milieu de la carte visible, au-dessus du volet (téléphone) : à + mi-hauteur de l'écran elle tombait sous l'échelle Leaflet posée sur le volet. */ +#compareBar span { position: absolute; top: calc((100% - var(--sheet-h)) / 2); left: 50%; transform: translate(-50%, -50%); width: 36px; height: 36px; border-radius: var(--r-full); display: flex; align-items: center; justify-content: center; background: var(--surface); color: var(--text); border: 1px solid var(--border); box-shadow: var(--shadow-sm); font-size: var(--fs-lg); } diff --git a/lidar_pipeline/web/map.js b/lidar_pipeline/web/map.js index 05e93b7..cfd9d19 100644 --- a/lidar_pipeline/web/map.js +++ b/lidar_pipeline/web/map.js @@ -425,15 +425,36 @@ function applyLayers() { updateRose(); } +// Plage de la séparation, en px du conteneur de la carte : la poignée reste +// dans la partie visible de la carte, jamais sous le panneau (ordinateur : +// panneau ouvert ou bande d'icônes, toute la hauteur) ni sous la colonne +// d'outils. COMPARE_MARGIN = demi-largeur de la poignée + marge. +const COMPARE_MARGIN = 24; +function compareBounds() { + const r = map.getContainer().getBoundingClientRect(); + let min = COMPARE_MARGIN, max = r.width - COMPARE_MARGIN; + if (!isPhone()) { + min = Math.max(min, el('panel').getBoundingClientRect().right - r.left + COMPARE_MARGIN); + max = Math.min(max, el('tools').getBoundingClientRect().left - r.left - COMPARE_MARGIN); + } + // Fenêtre trop étroite : la séparation reste au milieu de la plage. + if (min > max) min = max = (min + max) / 2; + return { left: r.left, min, max }; +} + // Découpe des couches de part et d'autre de la barre, en coordonnées de // calque (les conteneurs de tuiles se déplacent avec la carte) : recalculée // à chaque déplacement. Même couche des deux côtés : aucune découpe. +// STATE.compare.pos garde la valeur demandée (lien, glisser) ; la séparation +// affichée, barre et découpe ensemble, est bornée à compareBounds(). function applyCompareClip() { if (!STATE) return; const on = effectiveMode() === 'compare'; el('compareBar').hidden = !on; const size = map.getSize(); - const x = size.x * (on ? STATE.compare.pos : 50) / 100; + let x = size.x * (on ? STATE.compare.pos : 50) / 100; + const bounds = on ? compareBounds() : null; + if (bounds) x = Math.min(bounds.max, Math.max(bounds.min, x)); const nw = map.containerPointToLayerPoint([0, 0]); const se = map.containerPointToLayerPoint(size); const cx = map.containerPointToLayerPoint([x, 0]).x; @@ -448,13 +469,12 @@ function applyCompareClip() { } c.style.clipPath = clip; } - if (on) { - const r = map.getContainer().getBoundingClientRect(); - el('compareBar').style.left = (r.left + x) + 'px'; - } + if (on) el('compareBar').style.left = (bounds.left + x) + 'px'; } map.on('move', applyCompareClip); map.on('resize zoomend', applyCompareClip); +// Panneau replié, déplié, volet redimensionné : la plage de la barre change. +document.addEventListener('panel:tab', applyCompareClip); (function compareDrag() { const bar = el('compareBar'); @@ -462,8 +482,9 @@ map.on('resize zoomend', applyCompareClip); bar.addEventListener('pointerdown', (e) => { dragging = true; bar.setPointerCapture(e.pointerId); e.preventDefault(); }); bar.addEventListener('pointermove', (e) => { if (!dragging) return; - const r = map.getContainer().getBoundingClientRect(); - STATE.compare.pos = Math.min(95, Math.max(5, 100 * (e.clientX - r.left) / r.width)); + const b = compareBounds(); + const x = Math.min(b.max, Math.max(b.min, e.clientX - b.left)); + STATE.compare.pos = Math.min(95, Math.max(5, 100 * x / map.getSize().x)); applyCompareClip(); }); const end = () => { if (dragging) { dragging = false; saveState(); } };