[WIP] ✨ Started new Globe from scratch using OGL
This commit is contained in:
173
src/components/organisms/InteractiveGlobe2.svelte
Normal file
173
src/components/organisms/InteractiveGlobe2.svelte
Normal file
@@ -0,0 +1,173 @@
|
||||
<style lang="scss">
|
||||
@import "../../style/modules/globe2";
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { fade, fly } from 'svelte/transition'
|
||||
import { quartOut } from 'svelte/easing'
|
||||
import { Globe, type Marker } from '$modules/globe2'
|
||||
import { getRandomItem } from '$utils/functions'
|
||||
// Components
|
||||
import Image from '$components/atoms/Image.svelte'
|
||||
|
||||
let innerWidth: number
|
||||
let offsetWidth: number, offsetHeight: number
|
||||
let globeParentEl: HTMLElement, globeEl: HTMLElement
|
||||
let globe: any
|
||||
let observer: IntersectionObserver
|
||||
let animation: number
|
||||
let popinOpen: boolean = false
|
||||
let clusterLocations: Marker[] = []
|
||||
|
||||
$: globeResolution = innerWidth > 1440 && window.devicePixelRatio > 1 ? '4k' : '2k'
|
||||
|
||||
const { continents, locations } = getContext('global')
|
||||
const randomContinent: any = getRandomItem(continents.filter((cont: any) => cont.countries))
|
||||
const markers = locations.map(({ name, slug, country, coordinates: { coordinates }}): Marker => ({
|
||||
name,
|
||||
slug,
|
||||
country: {
|
||||
name: country.name,
|
||||
slug: country.slug,
|
||||
flag: country.flag,
|
||||
},
|
||||
lat: coordinates[1],
|
||||
lng: coordinates[0],
|
||||
}))
|
||||
|
||||
|
||||
onMount(() => {
|
||||
globe = new Globe({
|
||||
el: globeEl,
|
||||
parent: globeParentEl,
|
||||
width: offsetWidth,
|
||||
height: offsetHeight,
|
||||
mapFile: `/images/globe-map-${globeResolution}.png`,
|
||||
dpr: Math.min(Math.round(window.devicePixelRatio), 2),
|
||||
autoRotate: true,
|
||||
speed: 0.0035,
|
||||
rotationStart: randomContinent.rotation,
|
||||
markers,
|
||||
pane: import.meta.env.DEV,
|
||||
})
|
||||
|
||||
// Define cluster locations
|
||||
clusterLocations = locations.filter((loc: any) => loc.country.slug === 'france')
|
||||
|
||||
// console.log(globe)
|
||||
|
||||
resize()
|
||||
|
||||
// Render only if in viewport
|
||||
observer = new IntersectionObserver(entries => {
|
||||
entries.forEach(({ isIntersecting }: IntersectionObserverEntry) => {
|
||||
if (isIntersecting) {
|
||||
update()
|
||||
console.log('render globe2')
|
||||
} else {
|
||||
stop()
|
||||
console.log('stop globe2')
|
||||
}
|
||||
})
|
||||
}, {
|
||||
threshold: 0,
|
||||
rootMargin: '0px 0px 0px'
|
||||
})
|
||||
observer.observe(globeEl)
|
||||
|
||||
|
||||
// Destroy
|
||||
return () => {
|
||||
destroy()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* Methods
|
||||
*/
|
||||
// Update
|
||||
const update = () => {
|
||||
animation = requestAnimationFrame(update)
|
||||
globe.render()
|
||||
}
|
||||
|
||||
// Stop
|
||||
const stop = () => {
|
||||
cancelAnimationFrame(animation)
|
||||
}
|
||||
|
||||
// Resize
|
||||
const resize = () => {
|
||||
globe.resize({
|
||||
width: offsetWidth,
|
||||
height: offsetHeight,
|
||||
})
|
||||
}
|
||||
|
||||
// Destroy
|
||||
const destroy = () => {
|
||||
stop()
|
||||
globe.destroy()
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
on:resize={resize}
|
||||
/>
|
||||
|
||||
<div class="globe" bind:this={globeParentEl}>
|
||||
<div class="globe__inner">
|
||||
<div class="globe__canvas"
|
||||
bind:this={globeEl}
|
||||
bind:offsetWidth bind:offsetHeight
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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}" sveltekit:noscroll>
|
||||
<dl>
|
||||
<dt class="title-small">{name}</dt>
|
||||
<dd class="text-label text-label--small">{country.name}</dd>
|
||||
</dl>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
<li class="globe__cluster">
|
||||
<button on:click={() => popinOpen = !popinOpen} />
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{#if popinOpen}
|
||||
<div class="globe__popin" transition:fly={{ y: 16, duration: 500, easing: quartOut }}>
|
||||
<ul>
|
||||
{#each clusterLocations as { name, slug, country }, index (slug)}
|
||||
<li in:fade={{ duration: 400, delay: 200 + (50 * index) }}>
|
||||
<a href="/{country.slug}/{slug}" sveltekit:noscroll tabindex="0">
|
||||
<Image
|
||||
class="flag"
|
||||
id={country.flag.id}
|
||||
sizeKey="square-small"
|
||||
width={32} height={32}
|
||||
alt="Flag of {country.name}"
|
||||
/>
|
||||
<dl>
|
||||
<dt class="title-small">{name}</dt>
|
||||
<dd class="text-label text-label--small">{country.name}</dd>
|
||||
</dl>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<button class="close" aria-label="Close" on:click={() => popinOpen = false}>
|
||||
<svg width="9" height="9">
|
||||
<use xlink:href="#cross" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user