Remove old Globe
This commit is contained in:
@@ -3,140 +3,159 @@
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, getContext } from 'svelte'
|
||||
import { getPosition, getRandomItem } from '$utils/functions'
|
||||
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 scrollSmooth: number = 0.5
|
||||
export let opacity: number = 1
|
||||
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 InteractiveGlobe: any
|
||||
let globeEl: HTMLElement
|
||||
let observer: IntersectionObserver
|
||||
let globe: any
|
||||
let innerWidth: number
|
||||
let innerHeight: number
|
||||
let containerTop = 0
|
||||
let containerHeight = 0
|
||||
|
||||
$: globeResolution = innerWidth > 1440 && window.devicePixelRatio > 1 ? '4k' : '2k'
|
||||
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.filter((cont: any) => cont.countries))
|
||||
const markers = locations.map(({ name, slug, country, globe_close: isClose, coordinates: { coordinates }}: any) => ({
|
||||
const randomContinent: any = getRandomItem(continents)
|
||||
const markers = locations.map(({ name, slug, country, coordinates: { coordinates }}): Marker => ({
|
||||
name,
|
||||
slug,
|
||||
countryName: country.name,
|
||||
countrySlug: country.slug,
|
||||
country: { ...country },
|
||||
lat: coordinates[1],
|
||||
lng: coordinates[0],
|
||||
className: isClose ? 'is-close' : '',
|
||||
}))
|
||||
|
||||
|
||||
/*
|
||||
** Functions
|
||||
*/
|
||||
// Globe update
|
||||
const update = () => {
|
||||
requestAnimationFrame(update)
|
||||
globe.update()
|
||||
}
|
||||
onMount(() => {
|
||||
const globeResolution = innerWidth > 1440 && window.devicePixelRatio > 1 ? 4 : 2
|
||||
|
||||
// On scroll
|
||||
const handleScroll = () => {
|
||||
let scrollDiff = (containerTop + innerHeight + (containerHeight - innerHeight) / 2) - document.documentElement.scrollTop
|
||||
let scrollRatio = (1 - (scrollDiff / innerHeight)) * 2
|
||||
if (globe) {
|
||||
globe.updateCameraPos(scrollRatio, scrollDiff - innerHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// On resize
|
||||
const handleResize = () => {
|
||||
if (globeEl && globe) {
|
||||
containerTop = getPosition(globeEl).top
|
||||
containerHeight = globeEl.clientHeight
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
globe.resize()
|
||||
globe.update()
|
||||
handleScroll()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Run code when mounted
|
||||
*/
|
||||
onMount(async () => {
|
||||
// Import libraries and code
|
||||
const { default: InteractiveGlobe } = await import('$modules/globe')
|
||||
|
||||
// Init the globe from library
|
||||
globe = new InteractiveGlobe({
|
||||
globe = new Globe({
|
||||
el: globeEl,
|
||||
//cameraDistance: size, // Smaller number == larger globe
|
||||
autoRotationSpeed: autoRotate ? -0.0025 : 0,
|
||||
rotationStart: randomContinent.rotation, // In degrees
|
||||
scrollSmoothing: scrollSmooth,
|
||||
opacity: opacity,
|
||||
texture: `/images/globe-map-${globeResolution}.png`,
|
||||
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: true,
|
||||
speed,
|
||||
sunAngle: 2,
|
||||
rotationStart: randomContinent.rotation,
|
||||
enableMarkers,
|
||||
enableMarkersLinks: enableMarkersLinks && type !== 'cropped',
|
||||
markers,
|
||||
onLinkClicked: () => {}
|
||||
pane,
|
||||
})
|
||||
|
||||
// Run the globe
|
||||
update()
|
||||
setTimeout(() => {
|
||||
handleResize()
|
||||
handleScroll()
|
||||
}, 1000)
|
||||
resize()
|
||||
|
||||
// Render only if in viewport
|
||||
observer = new IntersectionObserver(([{ isIntersecting }]) => {
|
||||
if (isIntersecting) {
|
||||
update()
|
||||
|
||||
// Enable/Disable the globe when shown/hidden
|
||||
const globeCanvas = document.querySelector('.globe-canvas')
|
||||
|
||||
observer = new IntersectionObserver(entries => {
|
||||
entries.forEach(({ isIntersecting }: IntersectionObserverEntry) => {
|
||||
if (isIntersecting) {
|
||||
globe.enable()
|
||||
globeCanvas.classList.remove('is-hidden')
|
||||
} else {
|
||||
globe.disable()
|
||||
globeCanvas.classList.add('is-hidden')
|
||||
if (isDev) {
|
||||
console.log('globe: render/start')
|
||||
}
|
||||
})
|
||||
}, {
|
||||
threshold: 0,
|
||||
rootMargin: '0px 0px 0px'
|
||||
})
|
||||
} else {
|
||||
stop()
|
||||
|
||||
if (isDev) {
|
||||
console.log('globe: render/stop')
|
||||
}
|
||||
}
|
||||
}, { threshold: 0 })
|
||||
observer.observe(globeEl)
|
||||
|
||||
|
||||
// Destroy
|
||||
return () => {
|
||||
globe && globe.destroy()
|
||||
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
|
||||
on:scroll={handleScroll}
|
||||
on:resize={handleResize}
|
||||
bind:innerHeight
|
||||
bind:innerWidth
|
||||
<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>
|
||||
|
||||
<section id="globe">
|
||||
{#if type === 'cropped'}
|
||||
<div class="globe-cropped">
|
||||
<div class="globe" bind:this={globeEl} />
|
||||
{#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>
|
||||
{:else}
|
||||
<div class="globe" bind:this={globeEl} />
|
||||
{/if}
|
||||
</section>
|
||||
</div>
|
||||
Reference in New Issue
Block a user