162 lines
4.6 KiB
Svelte
162 lines
4.6 KiB
Svelte
<style lang="scss">
|
|
@import "../../style/modules/globe";
|
|
</style>
|
|
|
|
<script lang="ts">
|
|
import { getContext, onMount } from 'svelte'
|
|
import { fade, fly as flySvelte } from 'svelte/transition'
|
|
import { quartOut } from 'svelte/easing'
|
|
import { Globe, type Marker } from '$modules/globe'
|
|
import { getRandomItem, debounce } from '$utils/functions'
|
|
import reveal from '$animations/reveal'
|
|
// Components
|
|
import SplitText from '$components/SplitText.svelte'
|
|
|
|
const isDev = import.meta.env.DEV
|
|
|
|
export let type: string = undefined
|
|
export let autoRotate: boolean = true
|
|
export let enableMarkers: boolean = true
|
|
export let enableMarkersLinks: boolean = true
|
|
export let speed: number = 0.1
|
|
export let pane: boolean = isDev
|
|
export let width: number = undefined
|
|
|
|
let innerWidth: number
|
|
let globeParentEl: HTMLElement, globeEl: HTMLElement
|
|
let globe: any
|
|
let observer: IntersectionObserver
|
|
let animation: number
|
|
let hoveredMarker: { name: string, country: string } = null
|
|
|
|
const { continents, locations }: any = getContext('global')
|
|
const randomContinent: any = getRandomItem(continents)
|
|
const markers = locations.map(({ name, slug, country, coordinates: { coordinates }}): Marker => ({
|
|
name,
|
|
slug,
|
|
country: { ...country },
|
|
lat: coordinates[1],
|
|
lng: coordinates[0],
|
|
}))
|
|
|
|
|
|
onMount(() => {
|
|
const globeResolution = innerWidth > 1440 && window.devicePixelRatio > 1 ? 4 : 2
|
|
|
|
globe = new Globe({
|
|
el: globeEl,
|
|
parent: globeParentEl,
|
|
mapFile: `/images/globe-map-${globeResolution}k.png`,
|
|
mapFileDark: `/images/globe-map-dark-${globeResolution}k.png`,
|
|
dpr: Math.min(Math.round(window.devicePixelRatio), 2),
|
|
autoRotate,
|
|
speed,
|
|
sunAngle: 2,
|
|
rotationStart: randomContinent.rotation,
|
|
enableMarkers,
|
|
enableMarkersLinks: enableMarkersLinks && type !== 'cropped',
|
|
markers,
|
|
pane,
|
|
})
|
|
|
|
resize()
|
|
|
|
// Render only if in viewport
|
|
observer = new IntersectionObserver(([{ isIntersecting }]) => {
|
|
if (isIntersecting) {
|
|
update()
|
|
|
|
if (isDev) {
|
|
console.log('globe: render/start')
|
|
}
|
|
} else {
|
|
stop()
|
|
|
|
if (isDev) {
|
|
console.log('globe: render/stop')
|
|
}
|
|
}
|
|
}, { threshold: 0 })
|
|
observer.observe(globeEl)
|
|
|
|
|
|
// Destroy
|
|
return () => {
|
|
destroy()
|
|
observer && observer.disconnect()
|
|
}
|
|
})
|
|
|
|
|
|
/**
|
|
* Methods
|
|
*/
|
|
// Update
|
|
const update = () => {
|
|
animation = requestAnimationFrame(update)
|
|
globe.render()
|
|
}
|
|
|
|
// Stop
|
|
const stop = () => {
|
|
cancelAnimationFrame(animation)
|
|
}
|
|
|
|
// Resize
|
|
const resize = debounce(() => {
|
|
globe.resize()
|
|
}, 100)
|
|
|
|
// Destroy
|
|
const destroy = () => {
|
|
stop()
|
|
globe.destroy()
|
|
}
|
|
</script>
|
|
|
|
<svelte:window bind:innerWidth
|
|
on:resize={resize}
|
|
/>
|
|
|
|
<div class="globe" bind:this={globeParentEl}
|
|
class:is-cropped={type === 'cropped'}
|
|
style:--width={width ? `${width}px` : null}
|
|
>
|
|
<div class="globe__canvas" bind:this={globeEl}
|
|
class:is-faded={hoveredMarker}
|
|
>
|
|
<ul class="globe__markers">
|
|
{#each markers as { name, slug, country, lat, lng }}
|
|
<li class="globe__marker" data-location={slug} data-lat={lat} data-lng={lng}>
|
|
<a href="/{country.slug}/{slug}" data-sveltekit-noscroll
|
|
on:mouseenter={() => hoveredMarker = { name, country: country.name }}
|
|
on:mouseleave={() => hoveredMarker = null}
|
|
>
|
|
<i />
|
|
<span>{name}</span>
|
|
</a>
|
|
</li>
|
|
{/each}
|
|
</ul>
|
|
</div>
|
|
|
|
{#if hoveredMarker}
|
|
<div class="globe__location"
|
|
transition:fade={{ duration: 300, easing: quartOut }}
|
|
use:reveal={{
|
|
children: '.char',
|
|
animation: { y: ['110%', 0] },
|
|
options: {
|
|
stagger: 0.04,
|
|
duration: 1,
|
|
threshold: 0,
|
|
},
|
|
}}
|
|
>
|
|
<SplitText text={hoveredMarker.name} mode="chars" class="name" />
|
|
<p class="country" in:flySvelte={{ y: 16, duration: 800, easing: quartOut, delay: 900 }}>
|
|
{hoveredMarker.country}
|
|
</p>
|
|
</div>
|
|
{/if}
|
|
</div> |