diff --git a/frontend/css/styles.css b/frontend/css/styles.css index 2c16fb8..2b5b3c4 100644 --- a/frontend/css/styles.css +++ b/frontend/css/styles.css @@ -1149,6 +1149,9 @@ select { /* Dark map: invert ONLY the tile layer (overlays — track, route, roads, puck — keep their colours). User-tuned filter on OSM standard tiles. Applies to every Leaflet map. */ body.dark-map .leaflet-tile-pane { filter: invert(1) hue-rotate(180deg) brightness(1.5); } +/* Darken the map backdrop too, else the light Leaflet container (#ddd) flashes bright + between/behind tiles while zooming or where a tile is missing. */ +body.dark-map .leaflet-container { background: #1b1f24; } /* Always-on compass needle: red half = North, grey = South. Rotates to point to real north. Top-RIGHT (zoom controls are top-left), below the status-bar safe area. */ /* Position/direction puck (SVG divIcon) — no default leaflet-div-icon box. */ diff --git a/frontend/js/tilecache.js b/frontend/js/tilecache.js index e38cdda..fb07f84 100644 --- a/frontend/js/tilecache.js +++ b/frontend/js/tilecache.js @@ -130,29 +130,33 @@ TileCache.createLayer = function() { createTile: function(coords, done) { const tile = document.createElement("img"); const key = `${coords.z}/${coords.x}/${coords.y}`; + const url = `https://a.tile.openstreetmap.org/${key}.png`; + const show = (blob) => { tile.src = URL.createObjectURL(blob); done(null, tile); }; + // Fetch ONCE: cache and display from the same response. The old code loaded the + // display from the URL and cached via a SEPARATE fetch — on a flaky mobile + // link that second request often failed, so tiles showed but were never cached, + // and re-zooming re-fetched them. One fetch → every shown tile is also cached. + const fetchAndCache = () => { + fetch(url).then(async resp => { + if (!resp.ok) throw new Error("http " + resp.status); + const blob = await resp.blob(); + putTile(key, blob).catch(() => {}); + show(blob); + }).catch(() => { + // fetch blocked/failed → last resort plain (no caching) + tile.src = url; + tile.onload = () => done(null, tile); + tile.onerror = () => done(null, tile); + }); + }; getTile(key).then(blob => { - if (blob) { - tile.src = URL.createObjectURL(blob); - done(null, tile); - } else if (TileCache.offlineMode) { - // Offline: show empty tile - done(null, tile); - } else { - // Fetch from network - tile.crossOrigin = ""; - tile.src = `https://a.tile.openstreetmap.org/${key}.png`; - tile.onload = () => { - fetch(tile.src).then(r => r.blob()).then(b => putTile(key, b)).catch(() => {}); - done(null, tile); - }; - tile.onerror = () => done(null, tile); - } + if (blob) { show(blob); } + else if (TileCache.offlineMode) { done(null, tile); } // offline + not cached → empty + else { fetchAndCache(); } }).catch(() => { if (TileCache.offlineMode) { done(null, tile); return; } - tile.src = `https://a.tile.openstreetmap.org/${key}.png`; - tile.onload = () => done(null, tile); - tile.onerror = () => done(null, tile); + fetchAndCache(); }); return tile;