diff options
| author | Adam Soltys <[email protected]> | 2023-11-11 00:04:17 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2023-11-11 00:04:22 -0800 |
| commit | 8e278fadaf0d8a735e259d4d6c59ade581150692 (patch) | |
| tree | bd3e05d8cb29b72b512d68589c161fec4ad21cae /src | |
| parent | ae87f1d43c6d2cf34bd2ea6fd7b4d28af6d2aa91 (diff) | |
| download | coinos-ui-8e278fadaf0d8a735e259d4d6c59ade581150692.tar.gz coinos-ui-8e278fadaf0d8a735e259d4d6c59ade581150692.zip | |
map animation and list
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/AppHeader.svelte | 4 | ||||
| -rw-r--r-- | src/components/LocationsMap.svelte | 147 | ||||
| -rw-r--r-- | src/locales/en.json | 2 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.svelte | 2 |
4 files changed, 136 insertions, 19 deletions
diff --git a/src/components/AppHeader.svelte b/src/components/AppHeader.svelte index 4ccd2d03..3bf1e893 100644 --- a/src/components/AppHeader.svelte +++ b/src/components/AppHeader.svelte @@ -17,6 +17,8 @@ menuButtons = [ { key: 'nav.settings', icon: 'settings', href: `/${user.username}/settings` }, { key: 'nav.support', icon: 'support', href: `/support` }, + { key: 'nav.map', icon: 'map', href: `/${user.username}/map` }, + { key: 'nav.merch', icon: 'shop', href: `https://coinosmerch.com` }, { key: 'nav.signOut', icon: 'logout', href: `/logout` }, ]; @@ -53,7 +55,7 @@ .url.pathname === `/${user.username}/receive` ? 'opacity-100' : 'opacity-70 hover:opacity-80'}" - ><Icon icon="receive" style="mx-auto w-6 md:w-8" /> + ><Icon icon="numpad" style="mx-auto w-6 md:w-8" /> </button> </a> <a href={`/${user.username}/payments`}> diff --git a/src/components/LocationsMap.svelte b/src/components/LocationsMap.svelte index 7876659a..edd20632 100644 --- a/src/components/LocationsMap.svelte +++ b/src/components/LocationsMap.svelte @@ -2,6 +2,7 @@ import { onMount, onDestroy } from 'svelte'; import { browser } from '$app/environment'; import html2canvas from 'html2canvas'; + import { Icon } from '$comp'; import 'maplibre-gl/dist/maplibre-gl.css'; @@ -10,6 +11,71 @@ let mapContainer; let markers = []; + let currentIndex = -1; + let timeout; + + function select(m) { + stopFlying(); + currentIndex = inview.indexOf(m); + flyToMarker(m); + } + + async function flyToMarker(marker) { + marker.getElement().click(); + + map.flyTo({ + zoom: 14, + center: marker.getLngLat(), + essential: true, + easing: function (t) { + return 1 - Math.pow(1 - t, 3); + }, + duration: 5000 + }); + + await new Promise((r) => setTimeout(r, 5000)); + + if (!timeout) return; + + map.flyTo({ + zoom: 10.5, + center: marker.getLngLat(), + essential: true, + easing: function (t) { + return 1 - Math.pow(1 - t, 3); + }, + duration: 2000 + }); + } + + let toggle = () => { + if (timeout) stopFlying(); + else startFlying(); + }; + + let currentPopup; + function startFlying() { + currentIndex++; + + for (let p in popups) popups[p].remove(); + if (currentIndex >= inview.length) currentIndex = 0; + flyToMarker(inview[currentIndex]); + + timeout = setTimeout(startFlying, 7000); + } + + function stopFlying() { + if (!timeout) return; + timeout = clearTimeout(timeout); + + if (currentPopup) { + currentPopup.remove(); + } + + map.jumpTo({ center: map.getCenter(), zoom: map.getZoom() }); + map.off('moveend'); // Remove moveend listener if set + } + let checkAddress = (location) => { if (location['addr:housenumber'] && location['addr:street'] && location['addr:city']) { return `${ @@ -24,23 +90,24 @@ } }; + let inview = []; + function updateLabelVisibility() { + inview = []; if (!map) return; let zoom = map.getZoom(); let bounds = map.getBounds(); for (let marker of markers) { let isInView = bounds.contains(marker.getLngLat()); - let popup = marker.getPopup(); let { tags } = marker; - if (zoom > 13 && isInView) { - popup.setHTML(`<span class='block font-bold'>${tags.name}</span>`).addTo(map); - } else { - popup.remove(); - popup.setHTML(html(tags)); + if (isInView) { + inview.push(marker); } } + + inview = inview.sort((a, b) => a.tags.name.localeCompare(b.tags.name)); } let html = (tags) => `${ @@ -53,10 +120,13 @@ ${ tags && tags.website - ? `<a href=${tags.website} target="_blank" rel="noreferrer" class='block text-blue-400'>${tags.website}</a>` + ? `<a href=${tags.website} target="_blank" rel="noreferrer" class='block text-blue-400 break-all'>${tags.website}</a>` : '' }`; + let popups = {}; + let counter = 0; + onMount(() => { if (browser) { import('maplibre-gl').then(({ default: maplibre }) => { @@ -82,10 +152,16 @@ .setPopup(new maplibre.Popup().setHTML(html(tags))) .addTo(map); + marker.id = counter++; + marker.getElement().addEventListener('click', () => { let h = html(marker.tags); - let popup = marker.getPopup(); - popup.setHTML(h); + let p = marker.getPopup(); + popups[marker.id] = p; + p.setHTML(h); + p.on('close', () => { + delete popups[marker.id]; + }); }); marker.tags = tags; @@ -93,18 +169,31 @@ markers.push(marker); }); - // updateLabelVisibility(); - // map.on('moveend', updateLabelVisibility); - // map.on('zoomend', updateLabelVisibility); + 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); }); } }); onDestroy(() => { if (map) { - // map.off('moveend', updateLabelVisibility); - // map.off('zoomend', updateLabelVisibility); map.remove(); } }); @@ -113,12 +202,37 @@ <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 class="relative"> + <button + class="absolute flex top-2 right-2 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> + + <div class="absolute bottom-2 right-2 w-60 h-60 bg-white overflow-y-scroll p-4 bg-opacity-80"> + {#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> </div> + + {inview.length} </div> <style> :global(.maplibregl-popup-close-button) { - font-size: 18px; + font-size: 24px; padding: 8px; } @@ -131,11 +245,12 @@ } :global(.maplibregl-popup-content) { - padding: 0.5em 1em; + padding: 1em 2em; border-radius: 4px; font-size: 14px; color: black; background-color: white; overflow: hidden; + width: 280px; } </style> diff --git a/src/locales/en.json b/src/locales/en.json index 9ecd3702..2f629675 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -135,7 +135,7 @@ "messages": "Messages", "funds": "Funds", "map": "Map", - "merch": "Shop", + "merch": "Swag", "profile": "Profile", "startInSeconds": "Start in Seconds", "account": "Account", diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte index 180b12bd..e3150ff2 100644 --- a/src/routes/(app)/[username]/(profile)/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/+page.svelte @@ -99,7 +99,7 @@ class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-full" > <div class="mx-auto flex"> - <Icon icon="receive" style="my-auto h-6 mr-2" /> + <Icon icon="numpad" style="my-auto h-6 mr-2" /> <div class="my-auto mt-1">{$t('user.dashboard.receive')}</div> </div> </button> |
