It's been tricky but got there finally! Hello `:global` - Avoid using one global CSS file containing everything - Import the component SCSS file in a script tag from the component file to allow style scoping and including it only when used
136 lines
3.7 KiB
Svelte
136 lines
3.7 KiB
Svelte
<style lang="scss">
|
|
@import "../../style/modules/globe";
|
|
</style>
|
|
|
|
<script lang="ts">
|
|
import { onMount, onDestroy, getContext } from 'svelte'
|
|
import { getPosition, getRandomItem } from '$utils/functions'
|
|
|
|
export let type: string = undefined
|
|
export let autoRotate: boolean = true
|
|
export let scrollSmooth: number = 0.5
|
|
export let opacity: number = 1
|
|
|
|
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'
|
|
|
|
const { continents, locations } = 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) => ({
|
|
name,
|
|
slug,
|
|
countryName: country.name,
|
|
countrySlug: country.slug,
|
|
lat: coordinates[1],
|
|
lng: coordinates[0],
|
|
className: isClose ? 'is-close' : '',
|
|
}))
|
|
|
|
|
|
/*
|
|
** Functions
|
|
*/
|
|
// Globe update
|
|
const update = () => {
|
|
requestAnimationFrame(update)
|
|
globe.update()
|
|
}
|
|
|
|
// 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) {
|
|
containerTop = getPosition(globeEl).top
|
|
containerHeight = globeEl.clientHeight
|
|
}
|
|
if (globe) {
|
|
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({
|
|
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`,
|
|
markers,
|
|
onLinkClicked: () => {}
|
|
})
|
|
|
|
// Run the globe
|
|
update()
|
|
handleResize()
|
|
|
|
// Enable/Disable the globe when shown/hidden
|
|
observer = new IntersectionObserver(entries => {
|
|
entries.forEach(({ isIntersecting }: IntersectionObserverEntry) => {
|
|
if (isIntersecting) {
|
|
globe.enable()
|
|
console.log('globe: IO enable')
|
|
} else {
|
|
globe.disable()
|
|
console.log('globe: IO disable')
|
|
}
|
|
})
|
|
}, {
|
|
threshold: 0,
|
|
rootMargin: '0px 0px 0px'
|
|
})
|
|
observer.observe(globeEl)
|
|
})
|
|
|
|
|
|
/*
|
|
** Destroy when unmounted
|
|
*/
|
|
onDestroy(() => {
|
|
globe && globe.destroy()
|
|
})
|
|
</script>
|
|
|
|
<svelte:window
|
|
on:scroll={handleScroll}
|
|
on:resize={handleResize}
|
|
bind:innerHeight
|
|
bind:innerWidth
|
|
/>
|
|
|
|
|
|
<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> |