frontend/src/lib/components/Map.svelte
MuslemRahimi 25906cbb90 ui fixes
2025-01-31 12:15:30 +01:00

124 lines
3.0 KiB
Svelte

<script>
import { onMount, onDestroy } from "svelte";
import { browser } from "$app/environment";
import "ol/ol.css";
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import XYZ from "ol/source/XYZ";
import { fromLonLat } from "ol/proj";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import Feature from "ol/Feature";
import Point from "ol/geom/Point";
import { Style, Circle, Fill } from "ol/style";
import { defaults as defaultInteractions } from "ol/interaction";
// White House coordinates
const LON = -77.0365427;
const LAT = 38.8950368;
const ZOOM = 5;
let mapElement;
let map;
let animationFrame;
let pulseProgress = 0;
// Pulsing style function
const createPulseStyle = (progress) => {
return new Style({
image: new Circle({
radius: 6 + 9 * progress,
fill: new Fill({
color: `rgba(46, 134, 222, ${0.3 - 0.3 * progress})`,
}),
}),
});
};
// Animation loop
const animate = () => {
pulseProgress = (pulseProgress + 0.02) % 1;
vectorLayer
.getSource()
.getFeatures()[0]
.setStyle([
new Style({
image: new Circle({
radius: 6,
fill: new Fill({
color: "#2e86de",
}),
}),
}),
createPulseStyle(pulseProgress),
]);
animationFrame = requestAnimationFrame(animate);
};
let vectorLayer;
onMount(() => {
if (!browser) return;
const whiteHousePoint = new Feature({
geometry: new Point(fromLonLat([LON, LAT])),
});
vectorLayer = new VectorLayer({
source: new VectorSource({
features: [whiteHousePoint],
}),
});
map = new Map({
target: mapElement,
layers: [
new TileLayer({
source: new XYZ({
url: "https://{a-d}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png",
attributions:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>',
}),
}),
vectorLayer,
],
view: new View({
center: fromLonLat([LON, LAT]),
zoom: ZOOM,
minZoom: 2,
maxZoom: 18,
}),
controls: [],
interactions: defaultInteractions({
mouseWheelZoom: false, // Disable scroll zoom
pinchZoom: false, // Disable pinch-to-zoom
}),
});
animate();
return () => {
if (map) map.setTarget(undefined);
};
});
</script>
<div bind:this={mapElement} class="map-container" />
<style>
.map-container {
width: 100%;
height: 500px;
background-color: #1a1a1a;
overflow: hidden;
border-radius: 10px;
}
@media (max-width: 640px) {
.map-container {
height: 300px;
}
}
</style>