diff --git a/backend/static/css/components.css b/backend/static/css/components.css index 5d0369d..b9ae47e 100644 --- a/backend/static/css/components.css +++ b/backend/static/css/components.css @@ -2036,66 +2036,6 @@ textarea.form-control { .map-place-btns .btn { flex: 1; } /* Statusleiste: nur Info, unten links */ -/* Scan-Fortschrittsbalken — oben im Kartenfenster */ -.map-scan-bar { - position: absolute; - top: 0; - left: 0; - right: 0; - z-index: 1001; - height: 40px; - background: rgba(255,255,255,0.92); - backdrop-filter: blur(6px); - border-bottom: 1px solid var(--c-border-light); - display: flex; - align-items: center; - gap: var(--space-3); - padding: 0 var(--space-4); - opacity: 0; - pointer-events: none; - transition: opacity 0.25s; - overflow: hidden; -} -.map-scan-bar.active { - opacity: 1; -} -.map-scan-bar__track { - flex: 1; - height: 6px; - background: var(--c-border-light); - border-radius: var(--radius-full); - overflow: hidden; - position: relative; -} -.map-scan-bar__fill { - height: 6px; - background: var(--c-primary); - border-radius: var(--radius-full); - width: 0%; - transition: width 0.4s ease; - position: relative; -} -.map-scan-bar__fill::after { - content: ''; - position: absolute; - top: 0; right: 0; bottom: 0; - width: 60px; - background: linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent); - animation: scan-shimmer 1.2s ease-in-out infinite; -} -@keyframes scan-shimmer { - 0% { transform: translateX(-60px); opacity: 0; } - 50% { opacity: 1; } - 100% { transform: translateX(60px); opacity: 0; } -} -.map-scan-bar__label { - font-size: 12px; - font-weight: 600; - color: var(--c-primary); - min-width: 80px; - white-space: nowrap; -} - .map-statusbar { position: absolute; bottom: var(--space-3); diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 3dfcd92..24c0abe 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '97'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '98'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const App = (() => { diff --git a/backend/static/js/pages/map.js b/backend/static/js/pages/map.js index 8ac88b1..ede2804 100644 --- a/backend/static/js/pages/map.js +++ b/backend/static/js/pages/map.js @@ -183,13 +183,6 @@ window.Page_map = (() => { -
- -
-
-
-
-
@@ -412,29 +405,82 @@ window.Page_map = (() => { function _setOsmStatus(text, pct = null) { const el = document.getElementById('map-osm-status'); if (el) el.textContent = text; + _updateScanRing(text ? pct : null); + } - const bar = document.getElementById('map-scan-bar'); - const fill = document.getElementById('map-scan-fill'); - const label = document.getElementById('map-scan-label'); - if (!bar || !fill || !label) return; + function _updateScanRing(pct) { + const statusbar = document.getElementById('map-statusbar'); + if (!statusbar) return; + const mapEl = statusbar.closest('.map-main') || statusbar.parentElement; + if (!mapEl) return; - if (!text) { - bar.classList.remove('active'); + let svg = document.getElementById('map-scan-ring'); + + // Ring ausblenden / entfernen + if (pct === null) { + if (svg) { svg.style.opacity = '0'; setTimeout(() => svg?.remove(), 600); } + statusbar.style.border = ''; return; } - bar.classList.add('active'); - if (pct !== null) { - fill.style.width = `${pct}%`; - label.textContent = pct < 100 ? `Scanne ${pct}\u202f%` : ''; - } else { - fill.style.width = '30%'; - fill.classList.add('indeterminate'); - label.textContent = text; + + // SVG einmalig erzeugen + if (!svg) { + svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.id = 'map-scan-ring'; + svg.setAttribute('overflow', 'visible'); + svg.style.cssText = 'position:absolute;pointer-events:none;z-index:1002;transition:opacity 0.55s ease'; + const path = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + path.id = 'map-scan-ring-rect'; + path.setAttribute('fill', 'none'); + path.setAttribute('stroke', '#C4843A'); + path.setAttribute('stroke-width', '3'); + path.setAttribute('stroke-linecap', 'round'); + svg.appendChild(path); + mapEl.appendChild(svg); } - if (pct === 100) { - fill.style.width = '100%'; - label.textContent = ''; - setTimeout(() => bar.classList.remove('active'), 600); + + // Position relativ zum Map-Container berechnen + const sr = statusbar.getBoundingClientRect(); + const mr = mapEl.getBoundingClientRect(); + const w = sr.width; + const h = sr.height; + const r = h / 2; // border-radius-full = Hälfte der Höhe + const p = 2; // Abstand zur inneren Kante + + // Umfang der Pill: gerades Stück + zwei Halbkreise + const perim = 2 * (w - h) + Math.PI * h; + // Stroke beginnt oben-links und läuft im Uhrzeigersinn + // Um bei 12 Uhr zu starten: Offset um das linke Halbkreis-Viertel + halbe Geraden verschieben + const startShift = (w - h) / 2 + (Math.PI * h) / 4; + const progress = Math.min(100, Math.max(0, pct)); + const dashOffset = perim * (1 - progress / 100) + startShift; + + svg.style.left = (sr.left - mr.left - p) + 'px'; + svg.style.top = (sr.top - mr.top - p) + 'px'; + svg.style.width = (w + p * 2) + 'px'; + svg.style.height = (h + p * 2) + 'px'; + svg.style.opacity = '1'; + + const rect = document.getElementById('map-scan-ring-rect'); + rect.setAttribute('x', String(p)); + rect.setAttribute('y', String(p)); + rect.setAttribute('width', String(w)); + rect.setAttribute('height', String(h)); + rect.setAttribute('rx', String(r)); + rect.setAttribute('ry', String(r)); + rect.setAttribute('stroke-dasharray', perim.toFixed(2)); + rect.setAttribute('stroke-dashoffset', dashOffset.toFixed(2)); + + // Original-Rahmen verstecken während Ring aktiv ist + statusbar.style.border = 'none'; + + if (progress >= 100) { + setTimeout(() => { + const s = document.getElementById('map-scan-ring'); + if (s) s.style.opacity = '0'; + statusbar.style.border = ''; + setTimeout(() => s?.remove(), 600); + }, 500); } } diff --git a/backend/static/sw.js b/backend/static/sw.js index ff80e19..bbe91f9 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v122'; +const CACHE_VERSION = 'by-v123'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten