Routen-Detailkarte: WebGL-Kontext-Leak gefixt → bleibt GL + zoomt auf Route
Eigentliche Ursache von 'Detailkarte zoomt nicht auf die Route': die Karte war auf dem Gerät gar keine GL-Karte mehr, sondern der Leaflet+OSM-RASTER-Fallback. Grund: _detailMap (GL-Kontext) wurde beim Schließen des Modals NIE freigegeben — jede geöffnete Route leakte einen WebGL-Kontext. Nach ~8 wirft MapLibre, und UI.map.create fällt auf Leaflet+OSM zurück. Genau die Mapnik-Kacheln aus Renés Screenshots (und die OSM-Attribution, die wir doch loswerden wollten). Fixes: - _detailMap modulweit + im onClose des Detail-Modals freigeben. - routes.js destroy(): _detailMap/_suggestMap/_searchMap + Mini-Maps beim Verlassen der Seite freigeben. - ui.js: Offscreen-Snapshot-Kontext nach 15s Leerlauf freigeben (hielt dauerhaft einen Kontext; Cache bleibt → kein Neu-Rendern). - _fitRouteMap fittet jetzt aufs 'load'/'idle'-Event der Karte (iOS verwirft ein fitBounds VOR dem ersten Render) statt nur auf feste Timeouts. Verifiziert (headless): 12 Detail-Öffnungen in Folge bleiben ALLE GL (Leaflet:false), GL-Canvas-Zahl bleibt bei 1–2 statt zu wachsen. Vorher leakte jede Öffnung einen Kontext.
This commit is contained in:
parent
d203ab17a8
commit
720971d252
7 changed files with 71 additions and 30 deletions
|
|
@ -89,6 +89,7 @@ window.Page_routes = (() => {
|
|||
let _viewMode = 'list';
|
||||
let _searchMap = null; // L.map Instanz der Suchkarte
|
||||
let _searchLines = new Map(); // routeId → { line, route }
|
||||
let _detailMap = null; // GL-Karte im Detail-Modal (Kontext beim Schließen freigeben!)
|
||||
|
||||
// Mini-Karten auf den Route-Cards
|
||||
let _miniMaps = new Map(); // routeId → L.map
|
||||
|
|
@ -170,6 +171,14 @@ window.Page_routes = (() => {
|
|||
}
|
||||
function onDogChange() {}
|
||||
|
||||
// Beim Verlassen der Seite alle Listen-/Detail-Karten freigeben (WebGL-Kontext-Leak).
|
||||
// Aktive Navigations-/Aufzeichnungs-Overlays (_navMap/_recMap) bleiben unangetastet.
|
||||
function destroy() {
|
||||
[_detailMap, _suggestMap, _searchMap].forEach(m => { try { m && m.remove && m.remove(); } catch (e) {} });
|
||||
_detailMap = _suggestMap = _searchMap = null;
|
||||
try { _miniMaps.forEach(m => m.remove && m.remove()); _miniMaps.clear(); } catch (e) {}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// Render
|
||||
// ----------------------------------------------------------
|
||||
|
|
@ -2434,7 +2443,12 @@ window.Page_routes = (() => {
|
|||
</div>
|
||||
`;
|
||||
|
||||
UI.modal.open({ title: `🥾 ${UI.escape(route.name)}`, body, footer });
|
||||
// onClose: GL-Kontext der Detailkarte freigeben — sonst leakt jede geöffnete Route
|
||||
// einen WebGL-Kontext. Nach ~8 wirft MapLibre, und UI.map.create fällt auf
|
||||
// Leaflet+OSM-Raster zurück (genau das Symptom: Detailkarte plötzlich OSM-Raster
|
||||
// statt GL, und der Zoom passt nicht mehr).
|
||||
UI.modal.open({ title: `🥾 ${UI.escape(route.name)}`, body, footer,
|
||||
onClose: () => { if (_detailMap) { try { _detailMap.remove(); } catch (e) {} _detailMap = null; } } });
|
||||
|
||||
UI.ratingStars({
|
||||
containerId: `rk-rating-${route.id}`,
|
||||
|
|
@ -2552,8 +2566,8 @@ window.Page_routes = (() => {
|
|||
UI.noteModal('route', route.id, label, null);
|
||||
});
|
||||
|
||||
// Mini-Map
|
||||
let _detailMap = null;
|
||||
// Mini-Map (modulweite _detailMap → wird beim Schließen im onClose freigegeben)
|
||||
if (_detailMap) { try { _detailMap.remove(); } catch (e) {} _detailMap = null; }
|
||||
setTimeout(async () => {
|
||||
const el = document.getElementById('rk-detail-map');
|
||||
if (!el || !track.length) return;
|
||||
|
|
@ -2674,21 +2688,34 @@ window.Page_routes = (() => {
|
|||
}
|
||||
}
|
||||
|
||||
// Karte robust auf die ganze Route fitten — auch wenn der Container beim Erstellen
|
||||
// noch 0×0 ist (Modal-Animation / spätes Layout auf iOS). Feste Timeouts greifen dort
|
||||
// oft zu früh; der ResizeObserver fittet erneut, SOBALD der Container seine endgültige
|
||||
// Größe hat. Das war der Grund, warum die Detail-/Vorschlag-Karte auf dem Gerät beim
|
||||
// Start-Zoom (zoom 14, center=Start) hängen blieb statt auf die Route zu zoomen.
|
||||
// Karte robust auf die ganze Route fitten.
|
||||
// WICHTIG (iOS): MapLibre verwirft ein fitBounds, das VOR dem ersten Render läuft —
|
||||
// die Karte bleibt dann beim Start-Zoom (zoom 14, center=Start) hängen, statt auf die
|
||||
// Route zu zoomen. (In Headless-Chromium passiert das nicht, daher fiel es dort nicht
|
||||
// auf.) Deshalb fitten wir auf das 'load'/'idle'-Event der Karte — DANN ist sie wirklich
|
||||
// gerendert und der Fit bleibt. Feste Timeouts + ResizeObserver als Sicherheitsnetz.
|
||||
function _fitRouteMap(m, el, getBounds, opts) {
|
||||
opts = opts || { padding: [16, 16] };
|
||||
const fit = () => { try { m.invalidateSize(); m.fitBounds(getBounds(), opts); } catch (e) {} };
|
||||
opts = opts || { padding: [16, 16], maxZoom: 16 };
|
||||
let active = true;
|
||||
const sized = () => !el || (el.clientWidth > 0 && el.clientHeight > 0);
|
||||
const fit = () => { if (!active) return; try { m.invalidateSize(); m.fitBounds(getBounds(), opts); } catch (e) {} };
|
||||
const onReady = () => {
|
||||
if (!active) return;
|
||||
fit();
|
||||
// Erstes Ready-Event mit korrekt vermessenem Container = der gute Fit → danach Schluss,
|
||||
// damit der Nutzer frei zoomen/pannen kann.
|
||||
if (sized()) { active = false; try { m.off && m.off('idle', onReady); m.off && m.off('load', onReady); } catch (e) {} }
|
||||
};
|
||||
fit();
|
||||
setTimeout(fit, 150); setTimeout(fit, 400);
|
||||
[120, 350, 700, 1200, 2000].forEach(t => setTimeout(fit, t));
|
||||
try { m.on('load', onReady); } catch (e) {}
|
||||
try { m.on('idle', onReady); } catch (e) {}
|
||||
if (window.ResizeObserver && el) {
|
||||
const ro = new ResizeObserver(() => { if (el.clientWidth > 0 && el.clientHeight > 0) fit(); });
|
||||
const ro = new ResizeObserver(() => fit());
|
||||
ro.observe(el);
|
||||
setTimeout(() => { try { ro.disconnect(); } catch (e) {} }, 4000);
|
||||
}
|
||||
setTimeout(() => { active = false; }, 4000);
|
||||
}
|
||||
|
||||
async function _buildDetailMap(el, track) {
|
||||
|
|
@ -3220,6 +3247,6 @@ window.Page_routes = (() => {
|
|||
// Notiz-Modal (Custom DOM — kein UI.modal um Konflikte zu vermeiden)
|
||||
// ----------------------------------------------------------
|
||||
|
||||
return { init, refresh, onDogChange };
|
||||
return { init, refresh, onDogChange, destroy };
|
||||
|
||||
})();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue