your personal website on atproto - mirror blento.app
at main 168 lines 3.8 kB view raw
1<script lang="ts"> 2 import { onMount } from 'svelte'; 3 import 'mapbox-gl/dist/mapbox-gl.css'; 4 import mapboxgl from 'mapbox-gl'; 5 import { env } from '$env/dynamic/public'; 6 import type { Item } from '$lib/types'; 7 import { getHexOfCardColor } from '../helper'; 8 9 let { item = $bindable() }: { item: Item } = $props(); 10 11 $inspect(item); 12 13 let mapContainer: HTMLElement | undefined = $state(); 14 let map: mapboxgl.Map | undefined = $state(); 15 16 // Update light preset when changed in settings 17 $effect(() => { 18 const preset = item.cardData.lightPreset; 19 if (map && preset) { 20 map.setConfigProperty('basemap', 'lightPreset', preset); 21 } 22 }); 23 24 onMount(() => { 25 if (!mapContainer || !env.PUBLIC_MAPBOX_TOKEN) { 26 console.log('no map container or no mapbox token'); 27 } 28 29 try { 30 mapboxgl.accessToken = env.PUBLIC_MAPBOX_TOKEN; 31 32 const lat = parseFloat(item.cardData.lat); 33 const lon = parseFloat(item.cardData.lon); 34 const zoom = item.cardData.zoom ? parseFloat(item.cardData.zoom) : 0; 35 const lightPreset = item.cardData.lightPreset || 'day'; 36 37 map = new mapboxgl.Map({ 38 container: mapContainer, 39 style: 'mapbox://styles/mapbox/standard', 40 center: [lon, lat], 41 config: { 42 basemap: { 43 lightPreset: lightPreset, 44 showPointOfInterestLabels: false 45 } 46 }, 47 zoom: zoom, 48 attributionControl: false, 49 dragPan: false, 50 dragRotate: false, 51 keyboard: false, 52 doubleClickZoom: true, 53 touchZoomRotate: true, 54 scrollZoom: true, 55 boxZoom: false, 56 pitchWithRotate: false, 57 touchPitch: false 58 }); 59 60 // Keep location centered during zoom and save zoom level 61 map.on('zoom', () => { 62 if (map) { 63 map.setCenter([lon, lat]); 64 } 65 }); 66 67 map.on('zoomend', () => { 68 if (map) { 69 item.cardData.zoom = map.getZoom().toString(); 70 } 71 }); 72 73 map.on('load', () => { 74 if (!map) return; 75 76 map.resize(); 77 map.setCenter([lon, lat]); 78 79 const accentColor = getHexOfCardColor(item); 80 81 // Add location point source 82 map.addSource('location-point', { 83 type: 'geojson', 84 data: { 85 type: 'Feature', 86 geometry: { 87 type: 'Point', 88 coordinates: [lon, lat] 89 }, 90 properties: { 91 name: item.cardData.name || '' 92 } 93 } 94 }); 95 96 // Outer glow 97 map.addLayer({ 98 id: 'location-glow-outer', 99 type: 'circle', 100 source: 'location-point', 101 paint: { 102 'circle-radius': 20, 103 'circle-color': accentColor, 104 'circle-opacity': 0.15, 105 'circle-blur': 1 106 } 107 }); 108 109 // Middle glow 110 map.addLayer({ 111 id: 'location-glow-middle', 112 type: 'circle', 113 source: 'location-point', 114 paint: { 115 'circle-radius': 12, 116 'circle-color': accentColor, 117 'circle-opacity': 0.3, 118 'circle-blur': 0.5 119 } 120 }); 121 122 // White border 123 map.addLayer({ 124 id: 'location-dot-border', 125 type: 'circle', 126 source: 'location-point', 127 paint: { 128 'circle-radius': 8, 129 'circle-color': '#ffffff', 130 'circle-opacity': 1 131 } 132 }); 133 134 // Accent color center dot 135 map.addLayer({ 136 id: 'location-dot', 137 type: 'circle', 138 source: 'location-point', 139 paint: { 140 'circle-radius': 6, 141 'circle-color': accentColor, 142 'circle-opacity': 1 143 } 144 }); 145 }); 146 147 // Handle container resize 148 const resizeObserver = new ResizeObserver(() => { 149 if (map) { 150 map.resize(); 151 map.setCenter([lon, lat]); 152 } 153 }); 154 resizeObserver.observe(mapContainer); 155 156 return () => { 157 resizeObserver.disconnect(); 158 if (map) { 159 map.remove(); 160 } 161 }; 162 } catch (err) { 163 console.error(`Something went wrong trying to initialize the map`, err); 164 } 165 }); 166</script> 167 168<div bind:this={mapContainer} class="absolute inset-0 isolate z-50 h-full w-full"></div>