diff options
| author | Adam Soltys <[email protected]> | 2023-12-06 20:33:45 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2023-12-06 20:33:45 -0800 |
| commit | 6b3ff247e4125941a611369ed9bb74a2ffca57d8 (patch) | |
| tree | 91da939350398188834e5e002bc78011a2a8e5bf /src/components/LocationsMap.svelte | |
| parent | bb59ac3e4e7fcbf7f8a48820eab0ee21558183fc (diff) | |
| parent | d69c34cf389639eac82d24d1f1b02f038ef42399 (diff) | |
| download | coinos-ui-6b3ff247e4125941a611369ed9bb74a2ffca57d8.tar.gz coinos-ui-6b3ff247e4125941a611369ed9bb74a2ffca57d8.zip | |
Merge branch 'master' into swapee
Diffstat (limited to 'src/components/LocationsMap.svelte')
| -rw-r--r-- | src/components/LocationsMap.svelte | 368 |
1 files changed, 257 insertions, 111 deletions
diff --git a/src/components/LocationsMap.svelte b/src/components/LocationsMap.svelte index 7876659a..b41b1a73 100644 --- a/src/components/LocationsMap.svelte +++ b/src/components/LocationsMap.svelte @@ -1,141 +1,287 @@ <script> - import { onMount, onDestroy } from 'svelte'; - import { browser } from '$app/environment'; - import html2canvas from 'html2canvas'; + import { tick, onMount, onDestroy } from "svelte"; + import { browser } from "$app/environment"; + import Icon from "$comp/Icon.svelte"; + import Popup from "$comp/Popup.svelte"; + import { shuffleArray } from "$lib/utils"; - import 'maplibre-gl/dist/maplibre-gl.css'; + import "maplibre-gl/dist/maplibre-gl.css"; - export let locations; - let map; - let mapContainer; - let markers = []; + export let locations; + let map; + let mapContainer, mapWrapper; + let markers = []; - let checkAddress = (location) => { - if (location['addr:housenumber'] && location['addr:street'] && location['addr:city']) { - return `${ - location['addr:housenumber'] + ' ' + location['addr:street'] + ', ' + location['addr:city'] - }`; - } else if (location['addr:street'] && location['addr:city']) { - return `${location['addr:street'] + ', ' + location['addr:city']}`; - } else if (location['addr:city']) { - return `${location['addr:city']}`; - } else { - return ''; - } - }; + let currentIndex = -1; + let timeout; - function updateLabelVisibility() { - if (!map) return; - let zoom = map.getZoom(); - let bounds = map.getBounds(); + let full = () => { + if (!document.fullscreenElement) { + if (mapWrapper.requestFullscreen) { + mapWrapper.requestFullscreen(); + } else if (mapWrapper.mozRequestFullScreen) { + /* Firefox */ + mapWrapper.mozRequestFullScreen(); + } else if (mapWrapper.webkitRequestFullscreen) { + /* Chrome, Safari & Opera */ + mapWrapper.webkitRequestFullscreen(); + } else if (mapWrapper.msRequestFullscreen) { + /* IE/Edge */ + mapWrapper.msRequestFullscreen(); + } - for (let marker of markers) { - let isInView = bounds.contains(marker.getLngLat()); - let popup = marker.getPopup(); - let { tags } = marker; + map.resize(); + } else { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.mozCancelFullScreen) { + /* Firefox */ + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + /* Chrome, Safari and Opera */ + document.webkitExitFullscreen(); + } else if (document.msExitFullscreen) { + /* IE/Edge */ + document.msExitFullscreen(); + } + } + }; - if (zoom > 13 && isInView) { - popup.setHTML(`<span class='block font-bold'>${tags.name}</span>`).addTo(map); - } else { - popup.remove(); - popup.setHTML(html(tags)); - } - } - } + function select(m) { + stopFlying(); + currentIndex = inview.indexOf(m); + flyToMarker(m); + } - let html = (tags) => `${ - tags && tags.name ? `<span class='block font-bold'>${tags.name}</span>` : '' - } + async function flyToMarker(marker) { + map.flyTo({ + zoom: 12, + center: marker.getLngLat(), + essential: true, + easing: function (t) { + return 1 - Math.pow(1 - t, 3); + }, + duration: 3000, + }); - <span class='block'>${tags && checkAddress(tags)}</span> + await new Promise((r) => setTimeout(r, 3000)); + marker.getElement().click(); + await new Promise((r) => setTimeout(r, 1500)); - ${tags && tags.phone ? `<a href='tel:${tags.phone}' class='block'>${tags.phone}</a>` : ''} + if (!timeout) return; - ${ - tags && tags.website - ? `<a href=${tags.website} target="_blank" rel="noreferrer" class='block text-blue-400'>${tags.website}</a>` - : '' - }`; + map.flyTo({ + zoom: 10.5, + center: marker.getLngLat(), + essential: true, + easing: function (t) { + return 1 - Math.pow(1 - t, 3); + }, + duration: 1500, + }); + } - onMount(() => { - if (browser) { - import('maplibre-gl').then(({ default: maplibre }) => { - map = new maplibre.Map({ - container: mapContainer, - style: 'https://tiles.stadiamaps.com/styles/stamen_toner.json', - center: [-123.05, 49.26], - zoom: 10.5 - }); + let toggle = () => { + if (timeout) stopFlying(); + else startFlying(); + }; - map.on('load', () => { - locations.forEach((location) => { - if (location['deleted_at']) return; + let currentPopup; + function startFlying() { + currentIndex++; - let { lat, lon, tags } = location['osm_json']; - if (!(lat && lon)) return; + for (let p in popups) popups[p].remove(); + if (currentIndex >= inview.length) currentIndex = 0; + flyToMarker(inview[currentIndex]); - let element = document.createElement('div'); - element.className = 'marker'; + timeout = setTimeout(startFlying, 6000); + } - let marker = new maplibre.Marker({ color: '#F7931A' }) - .setLngLat([lon, lat]) - .setPopup(new maplibre.Popup().setHTML(html(tags))) - .addTo(map); + function stopFlying() { + if (!timeout) return; + timeout = clearTimeout(timeout); - marker.getElement().addEventListener('click', () => { - let h = html(marker.tags); - let popup = marker.getPopup(); - popup.setHTML(h); - }); + if (currentPopup) { + currentPopup.remove(); + } - marker.tags = tags; + map.jumpTo({ center: map.getCenter(), zoom: map.getZoom() }); + map.off("moveend"); // Remove moveend listener if set + } - markers.push(marker); - }); + let inview = []; - // updateLabelVisibility(); - // map.on('moveend', updateLabelVisibility); - // map.on('zoomend', updateLabelVisibility); - }); - }); - } - }); + function updateLabelVisibility() { + inview = []; + if (!map) return; + let zoom = map.getZoom(); + let bounds = map.getBounds(); - onDestroy(() => { - if (map) { - // map.off('moveend', updateLabelVisibility); - // map.off('zoomend', updateLabelVisibility); - map.remove(); - } - }); + for (let marker of markers) { + let isInView = bounds.contains(marker.getLngLat()); + let { tags } = marker; + + if (isInView) { + inview.push(marker); + } + } + + inview = inview.sort((a, b) => a.tags.name.localeCompare(b.tags.name)); + shuffleArray(inview); + } + + let popups = {}; + let counter = 0; + + onMount(() => { + if (browser) { + import("maplibre-gl").then(({ default: maplibre }) => { + map = new maplibre.Map({ + container: mapContainer, + style: "https://tiles.stadiamaps.com/styles/stamen_toner.json", + center: [-123.05, 49.26], + zoom: 10.5, + }); + + map.on("load", () => { + locations.forEach((location) => { + if (location["deleted_at"]) return; + + let { lat, lon, tags } = location["osm_json"]; + if (!(lat && lon)) return; + + let element = document.createElement("div"); + element.className = "marker"; + + let popupContainer = document.createElement("div"); + + let popup = new Popup({ + target: popupContainer, + props: { tags }, + }); + + let marker = new maplibre.Marker({ color: "#F7931A" }) + .setLngLat([lon, lat]) + .setPopup(new maplibre.Popup().setDOMContent(popupContainer)) + .addTo(map); + + marker.id = counter++; + + marker.getElement().addEventListener("click", () => { + let p = marker.getPopup(); + popups[marker.id] = p; + p.setDOMContent(popupContainer); + p.on("close", () => { + delete popups[marker.id]; + }); + }); + + marker.tags = tags; + + markers.push(marker); + }); + + updateLabelVisibility(); + + let observer = new IntersectionObserver( + (entries, observer) => { + entries.forEach((entry) => { + if (entry.isIntersecting) startFlying(); + else stopFlying(); + }); + }, + { threshold: 0.5 } + ); + + observer.observe(mapContainer); + }); + + map.on("mousedown", stopFlying); + map.on("touchstart", stopFlying); + map.on("wheel", stopFlying); + map.on("dragstart", stopFlying); + + let resize = () => map && map.resize(); + + document.addEventListener("fullscreenchange", resize, false); + document.addEventListener("webkitfullscreenchange", resize, false); + document.addEventListener("mozfullscreenchange", resize, false); + document.addEventListener("MSFullscreenChange", resize, false); + }); + } + }); + + onDestroy(() => map && map.remove()); </script> -<div class="container mx-auto max-w-4xl"> - <div class="flex w-full"> - <div class="mx-auto h-[550px] w-full z-0" bind:this={mapContainer} /> - </div> +<div class="container mx-auto p-2 space-y-5"> + <div class="flex w-full h-[60vh]" id="map" bind:this={mapWrapper}> + <div + id="map-container" + class="mx-auto h-full w-full z-0" + bind:this={mapContainer} + /> + <div class="relative"> + <div class="flex absolute flex top-2 right-2 gap-2"> + <button + class="rounded-full border-2 border-black bg-white w-10 h-10" + on:click={toggle} + > + {#if timeout} + <Icon icon="pause" style="w-4 m-auto" /> + {:else} + <Icon icon="play" style="w-4 m-auto" /> + {/if} + </button> + <button + class="rounded-full border-2 border-black bg-white w-10 h-10" + on:click={full} + > + <Icon icon="full" style="w-4 m-auto" /> + </button> + </div> + </div> + </div> + + <!-- <div class="hidden lg:grid grid-cols-3 justify-items-center"> --> + <!-- {#each inview as m} --> + <!-- <button --> + <!-- on:click={() => select(m)} --> + <!-- class="text-left" --> + <!-- class:font-bold={m.id === inview[currentIndex]?.id} --> + <!-- id={`marker-${m.id}`}>{m.tags.name}</button --> + <!-- > --> + <!-- {/each} --> + <!-- </div> --> </div> <style> - :global(.maplibregl-popup-close-button) { - font-size: 18px; - padding: 8px; - } + #map:fullscreen, + #map-container:fullscreen { + width: 100% !important; + height: 100% !important; + } + + :global(.maplibregl-popup-close-button) { + font-size: 24px; + padding: 8px; + } - :global(.maplibregl-popup :focus) { - outline: none; - } + :global(.maplibregl-popup :focus) { + outline: none; + } - :global(.maplibregl-ctrl-attrib) { - display: none; - } + :global(.maplibregl-ctrl-attrib) { + display: none; + } - :global(.maplibregl-popup-content) { - padding: 0.5em 1em; - border-radius: 4px; - font-size: 14px; - color: black; - background-color: white; - overflow: hidden; - } + :global(.maplibregl-popup-content) { + padding: 1em 2em; + border-radius: 4px; + font-size: 14px; + color: black; + background-color: white; + overflow: hidden; + width: 280px; + } </style> |
