feat(map): clean stop markers and route dimming

This commit is contained in:
2026-02-26 22:05:55 -05:00
parent 1f0229461b
commit c9a260ab23
7 changed files with 535 additions and 363 deletions

View File

@ -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,