Fix: Nutzerstandort auf der Karte anzeigen
Behebt Gitea Issue #2 "Standort auf Karte anzeigen": der per Browser-Geolocation ermittelte oder manuell eingegebene Standort (state.userLocation) wurde bisher nur zum Zentrieren/Entfernungsfilter verwendet, aber nie selbst auf der Karte dargestellt. Neuer, nicht interaktiver "Hier bist du"-Marker (blauer Punkt mit Puls-Ring, js/map/markers.js: createUserLocationMarker()) wird direkt auf die Karte gelegt (ausserhalb der clusterGroup, damit er nie mit Ausflugsziel-Markern geclustert wird) und reagiert reaktiv auf Aenderungen von state.userLocation in js/map/mapController.js.
This commit is contained in:
@@ -148,6 +148,51 @@
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
|
||||
/* "Hier bist du"-Marker für den übermittelten/manuell eingegebenen
|
||||
Nutzerstandort (Gitea Issue #2 "Standort auf Karte anzeigen"). */
|
||||
|
||||
.eventmap-user-marker {
|
||||
background: transparent;
|
||||
border: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.eventmap-user-marker__dot {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin: -7px 0 0 -7px;
|
||||
background: #1a73e8;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 50%;
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
|
||||
.eventmap-user-marker__pulse {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin: -11px 0 0 -11px;
|
||||
background: rgba(26, 115, 232, 0.35);
|
||||
border-radius: 50%;
|
||||
animation: eventmap-user-marker-pulse 2s ease-out infinite;
|
||||
}
|
||||
|
||||
@keyframes eventmap-user-marker-pulse {
|
||||
0% {
|
||||
transform: scale(0.6);
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.8);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Karten-Popup */
|
||||
|
||||
.eventmap-popup {
|
||||
|
||||
+40
-2
@@ -4,16 +4,18 @@
|
||||
|
||||
(function () {
|
||||
const { DEFAULT_MAP_CENTER } = EventMap.config;
|
||||
const { subscribe, setState, getFilteredPlaces } = EventMap.store;
|
||||
const { subscribe, getState, setState, getFilteredPlaces } = EventMap.store;
|
||||
const { getPlaceLatLng } = EventMap.models;
|
||||
const { createMarkerForPlace } = EventMap.map;
|
||||
const { createMarkerForPlace, createUserLocationMarker } = EventMap.map;
|
||||
|
||||
let map = null;
|
||||
let clusterGroup = null;
|
||||
let userLocationMarker = null;
|
||||
const markersByPlaceId = new Map();
|
||||
|
||||
let lastFilteredKey = null;
|
||||
let lastSelectedPlaceId = null;
|
||||
let lastUserLocationKey = null;
|
||||
|
||||
/** Günstiger Vergleichsschlüssel für eine gefilterte Trefferliste (Reihenfolge + IDs). */
|
||||
function computeFilteredKey(places) {
|
||||
@@ -72,6 +74,30 @@
|
||||
map.setView([lat, lng], zoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt den übermittelten/manuell eingegebenen Nutzerstandort als eigenen
|
||||
* Marker auf der Karte an (Gitea Issue #2). Läuft außerhalb der
|
||||
* clusterGroup, damit er nie mit Ausflugsziel-Markern geclustert wird und
|
||||
* immer sichtbar bleibt.
|
||||
*/
|
||||
function renderUserLocation(userLocation) {
|
||||
if (!userLocation) {
|
||||
if (userLocationMarker) {
|
||||
map.removeLayer(userLocationMarker);
|
||||
userLocationMarker = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const { lat, lng } = userLocation;
|
||||
if (userLocationMarker) {
|
||||
userLocationMarker.setLatLng([lat, lng]);
|
||||
} else {
|
||||
userLocationMarker = createUserLocationMarker(lat, lng);
|
||||
userLocationMarker.addTo(map);
|
||||
}
|
||||
}
|
||||
|
||||
function getMapInstance() {
|
||||
return map;
|
||||
}
|
||||
@@ -111,6 +137,10 @@
|
||||
fitToPlaces(initialFiltered);
|
||||
lastFilteredKey = computeFilteredKey(initialFiltered);
|
||||
|
||||
const initialUserLocation = getState().userLocation;
|
||||
renderUserLocation(initialUserLocation);
|
||||
lastUserLocationKey = initialUserLocation ? `${initialUserLocation.lat},${initialUserLocation.lng}` : null;
|
||||
|
||||
subscribe((state) => {
|
||||
// Filter/Standort können sich ändern, ohne dass sich state.places
|
||||
// ändert - daher bei jeder Store-Änderung neu filtern und nur bei
|
||||
@@ -128,6 +158,14 @@
|
||||
centerOnPlace(state.selectedPlaceId);
|
||||
}
|
||||
}
|
||||
|
||||
const userLocationKey = state.userLocation
|
||||
? `${state.userLocation.lat},${state.userLocation.lng}`
|
||||
: null;
|
||||
if (userLocationKey !== lastUserLocationKey) {
|
||||
lastUserLocationKey = userLocationKey;
|
||||
renderUserLocation(state.userLocation);
|
||||
}
|
||||
});
|
||||
|
||||
return map;
|
||||
|
||||
@@ -60,5 +60,34 @@
|
||||
return marker;
|
||||
}
|
||||
|
||||
let userLocationIcon = null;
|
||||
|
||||
function getUserLocationIcon() {
|
||||
if (!userLocationIcon) {
|
||||
userLocationIcon = L.divIcon({
|
||||
className: 'eventmap-user-marker',
|
||||
html: '<span class="eventmap-user-marker__pulse"></span><span class="eventmap-user-marker__dot"></span>',
|
||||
iconSize: [22, 22],
|
||||
iconAnchor: [11, 11],
|
||||
});
|
||||
}
|
||||
return userLocationIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt einen (nicht klickbaren) Marker für den übermittelten/manuell
|
||||
* eingegebenen Nutzerstandort (Gitea Issue #2 "Standort auf Karte
|
||||
* anzeigen"). Bewusst kein Popup/Interaktion, da es kein Ausflugsziel ist.
|
||||
*/
|
||||
function createUserLocationMarker(lat, lng) {
|
||||
return L.marker([lat, lng], {
|
||||
icon: getUserLocationIcon(),
|
||||
interactive: false,
|
||||
keyboard: false,
|
||||
zIndexOffset: 1000,
|
||||
});
|
||||
}
|
||||
|
||||
EventMap.map.createMarkerForPlace = createMarkerForPlace;
|
||||
EventMap.map.createUserLocationMarker = createUserLocationMarker;
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user