130 lines
3.5 KiB
Svelte
130 lines
3.5 KiB
Svelte
<script lang="ts">
|
|
import { onMount, getContext } from 'svelte'
|
|
import { getPosition, getRandomElement } from '$utils/functions'
|
|
|
|
export let type: string = undefined
|
|
export let autoRotate: boolean = true
|
|
export let scrollSmooth: number = 0.5
|
|
export let opacity: number = 1
|
|
|
|
const { continents, locations } = getContext('global')
|
|
|
|
let globe: any
|
|
let Globe: any
|
|
let globeEl: HTMLElement
|
|
let windowHeight: number, windowWidth: number
|
|
let containerTop: number = 0, containerHeight: number = 0
|
|
let observer: IntersectionObserver
|
|
|
|
const randomContinent = getRandomElement(continents.filter(cont => cont.countries))
|
|
const globeResolution = windowWidth > 1440 && window.devicePixelRatio > 1 ? '4k' : '2k'
|
|
const markers = locations.map(({ name, slug, country, coordinates: { coordinates }}: any) => ({
|
|
name,
|
|
slug,
|
|
countryName: country.name,
|
|
countrySlug: country.slug,
|
|
lat: coordinates[1],
|
|
lng: coordinates[0],
|
|
// className: location.close ? 'is-close' : '',
|
|
}))
|
|
|
|
|
|
/**
|
|
* Globe update
|
|
*/
|
|
const update = () => {
|
|
requestAnimationFrame(update)
|
|
globe.update()
|
|
}
|
|
|
|
/**
|
|
* When scrolling
|
|
*/
|
|
const handleScroll = () => {
|
|
let scrollDiff = (containerTop + windowHeight + (containerHeight - windowHeight) / 2) - document.documentElement.scrollTop
|
|
let scrollRatio = (1 - (scrollDiff / windowHeight)) * 2
|
|
if (globe) {
|
|
globe.updateCameraPos(scrollRatio, scrollDiff - windowHeight)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* When resizing
|
|
*/
|
|
const handleResize = () => {
|
|
if (globeEl) {
|
|
containerTop = getPosition(globeEl).top
|
|
containerHeight = globeEl.clientHeight
|
|
}
|
|
if (globe) {
|
|
globe.resize()
|
|
globe.update()
|
|
}
|
|
handleScroll()
|
|
}
|
|
|
|
|
|
onMount(async () => {
|
|
// Load globe library
|
|
Globe = await import('$modules/globe')
|
|
|
|
// Instantiate globe
|
|
globe = new Globe.default({
|
|
el: globeEl,
|
|
//cameraDistance: size, // Smaller number == larger globe
|
|
autoRotationSpeed: autoRotate ? -0.0025 : 0,
|
|
rotationStart: randomContinent.rotation, // In degrees
|
|
scrollSmoothing: scrollSmooth,
|
|
opacity,
|
|
texture: `/images/globe-map-${globeResolution}.png`,
|
|
markers,
|
|
onLinkClicked: () => {}
|
|
})
|
|
|
|
// Observe the globe
|
|
observer = new IntersectionObserver(entries => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
globe.enable()
|
|
} else {
|
|
globe.disable()
|
|
}
|
|
})
|
|
}, {
|
|
threshold: 0,
|
|
rootMargin: '0px 0px 0px'
|
|
})
|
|
observer.observe(globeEl)
|
|
|
|
// Run the globe
|
|
update()
|
|
handleResize()
|
|
|
|
|
|
// Destroy
|
|
return () => {
|
|
if (globe) {
|
|
globe.destroy()
|
|
observer.disconnect()
|
|
}
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<svelte:window
|
|
bind:innerHeight={windowHeight}
|
|
bind:innerWidth={windowWidth}
|
|
on:scroll={handleScroll}
|
|
on:resize={handleResize}
|
|
/>
|
|
|
|
|
|
<section id="globe">
|
|
{#if type === 'cropped'}
|
|
<div class="globe--cropped">
|
|
<div class="globe" bind:this={globeEl} />
|
|
</div>
|
|
{:else}
|
|
<div class="globe" bind:this={globeEl} />
|
|
{/if}
|
|
</section> |