Fix: Routen-Cards mit OSM-Mini-Karte statt SVG, Username ohne Prefix
- Jede Route-Card zeigt echte OSM-Tiles als Kartenvorschau (Leaflet lazy-laden + IntersectionObserver → tiles erst wenn sichtbar) - Track (orange), Start (grün), Ziel (rot) als Overlay - Interaktion komplett deaktiviert (drag/zoom/click off) - Username ohne "von " — kürzer, kein redundanter Text - _svgPreview() bleibt als interner Fallback erhalten
This commit is contained in:
parent
ebe4ce20cf
commit
96bd57f0ad
1 changed files with 77 additions and 8 deletions
|
|
@ -17,6 +17,10 @@ window.Page_routes = (() => {
|
|||
let _sortBy = 'newest';
|
||||
let _onlyMine = false;
|
||||
|
||||
// Mini-Karten auf den Route-Cards
|
||||
let _miniMaps = new Map(); // routeId → L.map
|
||||
let _leafletReady = false;
|
||||
|
||||
const DIFFICULTY_LABEL = { leicht: '🟢 Leicht', mittel: '🟡 Mittel', anspruchsvoll: '🔴 Anspruchsvoll' };
|
||||
const TERRAIN_LABEL = { wald: '🌲 Wald', asphalt: '🛣️ Asphalt', wiese: '🌿 Wiese', mix: '🔀 Mix' };
|
||||
const HUNDE_LABEL = { eingeschränkt: '🐾', gut: '🐾🐾', sehr_gut: '🐾🐾🐾', premium: '🐾🐾🐾🐾' };
|
||||
|
|
@ -37,10 +41,26 @@ window.Page_routes = (() => {
|
|||
_container = container;
|
||||
_appState = appState;
|
||||
_render();
|
||||
_loadLeaflet(); // fire & forget — bereit wenn Cards gerendert werden
|
||||
try { _userPos = await API.getLocation(); } catch {}
|
||||
_loadData();
|
||||
}
|
||||
|
||||
async function _loadLeaflet() {
|
||||
if (_leafletReady || window.L) { _leafletReady = true; return; }
|
||||
if (!document.querySelector('link[href="/css/leaflet.css"]')) {
|
||||
const l = document.createElement('link');
|
||||
l.rel = 'stylesheet'; l.href = '/css/leaflet.css';
|
||||
document.head.appendChild(l);
|
||||
}
|
||||
await new Promise((res, rej) => {
|
||||
const s = document.createElement('script');
|
||||
s.src = '/js/leaflet.js'; s.onload = res; s.onerror = rej;
|
||||
document.head.appendChild(s);
|
||||
});
|
||||
_leafletReady = true;
|
||||
}
|
||||
|
||||
function refresh() { _loadData(); }
|
||||
function onDogChange() {}
|
||||
|
||||
|
|
@ -205,7 +225,12 @@ window.Page_routes = (() => {
|
|||
}
|
||||
return;
|
||||
}
|
||||
// Alte Mini-Maps zerstören bevor DOM neu geschrieben wird
|
||||
_miniMaps.forEach(m => m.remove());
|
||||
_miniMaps.clear();
|
||||
|
||||
grid.innerHTML = _filtered.map(r => _cardHTML(r)).join('');
|
||||
_initMiniMaps();
|
||||
grid.querySelectorAll('.rk-card').forEach(card => {
|
||||
card.addEventListener('click', e => {
|
||||
if (e.target.closest('.rk-stars,.rk-dl-btn')) return;
|
||||
|
|
@ -236,16 +261,16 @@ window.Page_routes = (() => {
|
|||
const paws = HUNDE_LABEL[r.hunde_tauglichkeit] || '';
|
||||
const dist = r.distanz_km ? `${r.distanz_km.toFixed(1)} km` : '';
|
||||
const dur = r.dauer_min ? _fmtDur(r.dauer_min) : '';
|
||||
const preview = _svgPreview(r.preview_track || []);
|
||||
const firstPhoto = (r.foto_urls || [])[0];
|
||||
const previewContent = firstPhoto
|
||||
? `<img src="${_esc(firstPhoto)}" style="width:100%;height:100%;object-fit:cover">`
|
||||
: `<div class="rk-mini-map" data-id="${r.id}"
|
||||
data-track='${JSON.stringify(r.preview_track||[])}'
|
||||
style="width:100%;height:100%"></div>`;
|
||||
|
||||
return `
|
||||
<div class="rk-card" data-id="${r.id}">
|
||||
<div class="rk-card-preview">
|
||||
${firstPhoto
|
||||
? `<img src="${_esc(firstPhoto)}" style="width:100%;height:100%;object-fit:cover">`
|
||||
: preview}
|
||||
</div>
|
||||
<div class="rk-card-preview">${previewContent}</div>
|
||||
<div class="rk-card-body">
|
||||
<div class="rk-card-name">${_esc(r.name)}</div>
|
||||
<div class="rk-card-stats">
|
||||
|
|
@ -263,7 +288,7 @@ window.Page_routes = (() => {
|
|||
<div class="rk-card-footer">
|
||||
<div class="rk-stars">${_starsHTML(r.id, r.bewertung||0, r.anz_bewertungen||0)}</div>
|
||||
<div class="rk-card-actions">
|
||||
<span class="rk-card-author">von ${_esc(r.user_name||'Anonym')}</span>
|
||||
<span class="rk-card-author">${_esc(r.user_name||'Anonym')}</span>
|
||||
<button class="rk-dl-btn" data-id="${r.id}">⬇ GPX</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -279,7 +304,51 @@ window.Page_routes = (() => {
|
|||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// SVG-Vorschau
|
||||
// Mini-Karten (OSM-Tiles via Leaflet, lazy per IntersectionObserver)
|
||||
// ----------------------------------------------------------
|
||||
function _initMiniMaps() {
|
||||
const init = () => {
|
||||
const obs = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (!entry.isIntersecting) return;
|
||||
obs.unobserve(entry.target);
|
||||
_buildMiniMap(entry.target);
|
||||
});
|
||||
}, { rootMargin: '150px' });
|
||||
document.querySelectorAll('.rk-mini-map').forEach(el => obs.observe(el));
|
||||
};
|
||||
|
||||
if (window.L) { init(); return; }
|
||||
// Leaflet noch am Laden — kurz pollen
|
||||
let tries = 0;
|
||||
const poll = setInterval(() => {
|
||||
if (window.L || ++tries > 30) { clearInterval(poll); if (window.L) init(); }
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function _buildMiniMap(el) {
|
||||
const track = JSON.parse(el.dataset.track || '[]');
|
||||
const routeId = parseInt(el.dataset.id);
|
||||
if (track.length < 2) {
|
||||
el.innerHTML = '<div class="rk-preview-empty">🗺️</div>';
|
||||
return;
|
||||
}
|
||||
const lls = track.map(p => [p.lat, p.lon]);
|
||||
const m = L.map(el, {
|
||||
zoomControl: false, attributionControl: false,
|
||||
dragging: false, touchZoom: false, scrollWheelZoom: false,
|
||||
doubleClickZoom: false, keyboard: false, boxZoom: false,
|
||||
});
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 17 }).addTo(m);
|
||||
const poly = L.polyline(lls, { color: '#C4843A', weight: 3, opacity: 0.9 }).addTo(m);
|
||||
L.circleMarker(lls[0], { radius: 5, color: '#22C55E', fillColor: '#22C55E', fillOpacity: 1, weight: 1.5 }).addTo(m);
|
||||
L.circleMarker(lls.at(-1), { radius: 5, color: '#EF4444', fillColor: '#EF4444', fillOpacity: 1, weight: 1.5 }).addTo(m);
|
||||
m.fitBounds(poly.getBounds(), { padding: [8, 8] });
|
||||
_miniMaps.set(routeId, m);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// SVG-Vorschau (Fallback, wird nicht mehr direkt genutzt)
|
||||
// ----------------------------------------------------------
|
||||
function _svgPreview(track) {
|
||||
if (!track || track.length < 2)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue