feat(map): clean stop markers and route dimming
This commit is contained in:
@ -208,6 +208,160 @@ export function useGoogleMaps() {
|
||||
return marker
|
||||
}
|
||||
|
||||
function addCleanMarker(
|
||||
position: { lat: number; lng: number },
|
||||
title: string,
|
||||
type: 'normal' | 'cercana' | 'origen' | 'destino',
|
||||
onClick?: () => void
|
||||
): google.maps.Marker | null {
|
||||
if (!map.value) {
|
||||
console.error('Map not initialized');
|
||||
return null;
|
||||
}
|
||||
|
||||
const iconoParadaNormal = {
|
||||
path: google.maps.SymbolPath.CIRCLE,
|
||||
fillColor: '#3B82F6', // azul
|
||||
fillOpacity: 1,
|
||||
strokeColor: '#FFFFFF', // borde blanco limpio
|
||||
strokeWeight: 2,
|
||||
scale: 7
|
||||
};
|
||||
|
||||
const iconoParadaCercana = {
|
||||
path: google.maps.SymbolPath.CIRCLE,
|
||||
fillColor: '#F59E0B', // amarillo/naranja
|
||||
fillOpacity: 1,
|
||||
strokeColor: '#FFFFFF',
|
||||
strokeWeight: 3,
|
||||
scale: 10
|
||||
};
|
||||
|
||||
const iconoOrigen = {
|
||||
path: google.maps.SymbolPath.CIRCLE,
|
||||
fillColor: '#10B981', // verde
|
||||
fillOpacity: 1,
|
||||
strokeColor: '#FFFFFF',
|
||||
strokeWeight: 3,
|
||||
scale: 10
|
||||
};
|
||||
|
||||
const iconoDestino = {
|
||||
path: google.maps.SymbolPath.CIRCLE,
|
||||
fillColor: '#EF4444', // rojo
|
||||
fillOpacity: 1,
|
||||
strokeColor: '#FFFFFF',
|
||||
strokeWeight: 3,
|
||||
scale: 10
|
||||
};
|
||||
|
||||
const iconos = {
|
||||
normal: iconoParadaNormal,
|
||||
cercana: iconoParadaCercana,
|
||||
origen: iconoOrigen,
|
||||
destino: iconoDestino
|
||||
};
|
||||
|
||||
const marker = new google.maps.Marker({
|
||||
position,
|
||||
map: map.value,
|
||||
title,
|
||||
icon: iconos[type],
|
||||
});
|
||||
|
||||
if (onClick) {
|
||||
const infoWindow = new google.maps.InfoWindow({
|
||||
content: `
|
||||
<div style="font-family: sans-serif; padding: 6px 10px; font-size: 13px; font-weight: 600; color: #1E3A5F; white-space: nowrap;">
|
||||
🚌 ${title}
|
||||
</div>
|
||||
`
|
||||
});
|
||||
marker.addListener('click', () => {
|
||||
infoWindow.open(map.value, marker);
|
||||
onClick();
|
||||
});
|
||||
}
|
||||
|
||||
if (map.value) {
|
||||
if (!globalOverlays.has(map.value)) {
|
||||
globalOverlays.set(map.value, new Set());
|
||||
}
|
||||
globalOverlays.get(map.value)!.add(marker);
|
||||
}
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
function addHtmlMarker(
|
||||
position: { lat: number; lng: number },
|
||||
htmlContent: string,
|
||||
offset: { x: number; y: number } = { x: 0, y: 0 }
|
||||
) {
|
||||
if (!map.value) return null;
|
||||
|
||||
class CustomOverlay extends google.maps.OverlayView {
|
||||
private div: HTMLElement | null = null;
|
||||
private pos: google.maps.LatLng;
|
||||
|
||||
constructor(pos: google.maps.LatLng) {
|
||||
super();
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
onAdd() {
|
||||
const div = document.createElement('div');
|
||||
div.style.position = 'absolute';
|
||||
div.style.cursor = 'pointer';
|
||||
div.innerHTML = htmlContent;
|
||||
this.div = div;
|
||||
const panes = this.getPanes();
|
||||
panes?.overlayMouseTarget.appendChild(div);
|
||||
}
|
||||
|
||||
draw() {
|
||||
const overlayProjection = this.getProjection();
|
||||
const point = overlayProjection.fromLatLngToDivPixel(this.pos);
|
||||
if (point && this.div) {
|
||||
this.div.style.left = (point.x + offset.x) + 'px';
|
||||
this.div.style.top = (point.y + offset.y) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
onRemove() {
|
||||
if (this.div) {
|
||||
try {
|
||||
// Safer element removal
|
||||
if (this.div.parentNode) {
|
||||
this.div.parentNode.removeChild(this.div);
|
||||
} else {
|
||||
this.div.remove();
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('CustomOverlay: element already removed or parent mismatch', e);
|
||||
}
|
||||
this.div = null;
|
||||
}
|
||||
}
|
||||
|
||||
setPosition(newPos: { lat: number; lng: number }) {
|
||||
this.pos = new google.maps.LatLng(newPos.lat, newPos.lng);
|
||||
this.draw();
|
||||
}
|
||||
}
|
||||
|
||||
const overlay = new CustomOverlay(new google.maps.LatLng(position.lat, position.lng));
|
||||
overlay.setMap(map.value);
|
||||
|
||||
// Track for cleanup
|
||||
if (!globalOverlays.has(map.value)) {
|
||||
globalOverlays.set(map.value, new Set());
|
||||
}
|
||||
globalOverlays.get(map.value)!.add(overlay as any);
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
function addPolyline(path: Array<{ lat: number; lng: number }>): google.maps.Polyline | null {
|
||||
if (!map.value) {
|
||||
console.error('Map not initialized')
|
||||
@ -362,75 +516,6 @@ export function useGoogleMaps() {
|
||||
// with Google Maps' native OverlayView management.
|
||||
}
|
||||
|
||||
function addHtmlMarker(
|
||||
position: { lat: number; lng: number },
|
||||
htmlContent: string,
|
||||
offset: { x: number; y: number } = { x: 0, y: 0 }
|
||||
) {
|
||||
if (!map.value) return null;
|
||||
|
||||
class CustomOverlay extends google.maps.OverlayView {
|
||||
private div: HTMLElement | null = null;
|
||||
private pos: google.maps.LatLng;
|
||||
|
||||
constructor(pos: google.maps.LatLng) {
|
||||
super();
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
onAdd() {
|
||||
const div = document.createElement('div');
|
||||
div.style.position = 'absolute';
|
||||
div.style.cursor = 'pointer';
|
||||
div.innerHTML = htmlContent;
|
||||
this.div = div;
|
||||
const panes = this.getPanes();
|
||||
panes?.overlayMouseTarget.appendChild(div);
|
||||
}
|
||||
|
||||
draw() {
|
||||
const overlayProjection = this.getProjection();
|
||||
const point = overlayProjection.fromLatLngToDivPixel(this.pos);
|
||||
if (point && this.div) {
|
||||
this.div.style.left = (point.x + offset.x) + 'px';
|
||||
this.div.style.top = (point.y + offset.y) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
onRemove() {
|
||||
if (this.div) {
|
||||
try {
|
||||
// Safer element removal
|
||||
if (this.div.parentNode) {
|
||||
this.div.parentNode.removeChild(this.div);
|
||||
} else {
|
||||
this.div.remove();
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('CustomOverlay: element already removed or parent mismatch', e);
|
||||
}
|
||||
this.div = null;
|
||||
}
|
||||
}
|
||||
|
||||
setPosition(newPos: { lat: number; lng: number }) {
|
||||
this.pos = new google.maps.LatLng(newPos.lat, newPos.lng);
|
||||
this.draw();
|
||||
}
|
||||
}
|
||||
|
||||
const overlay = new CustomOverlay(new google.maps.LatLng(position.lat, position.lng));
|
||||
overlay.setMap(map.value);
|
||||
|
||||
// Track for cleanup
|
||||
if (!globalOverlays.has(map.value)) {
|
||||
globalOverlays.set(map.value, new Set());
|
||||
}
|
||||
globalOverlays.get(map.value)!.add(overlay as any);
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadMaps()
|
||||
})
|
||||
@ -444,6 +529,7 @@ export function useGoogleMaps() {
|
||||
addMarker,
|
||||
addHtmlMarker,
|
||||
addNumberedMarker,
|
||||
addCleanMarker,
|
||||
addPolyline,
|
||||
addRoutePolyline,
|
||||
fitBounds,
|
||||
|
||||
Reference in New Issue
Block a user