<script type="module">
import createGlobe from 'https://cdn.skypack.dev/cobe'
function waitForCanvas() {
const canvas = document.getElementById('globe-canvas')
if (!canvas) {
setTimeout(waitForCanvas, 100)
return
}
let pointerInteracting = false
let lastPointerX = 0
let userVelocity = 0
let phi = 0
let theta = 0
let globe = null
const autoRotationSpeed = 0.010
const friction = 0.95
const sensitivity = 0.012
const minVelocityThreshold = 0.001
function getCanvasSize() {
const container = document.getElementById('globe-container')
return container ? container.offsetWidth : 500
}
function initGlobe() {
const width = getCanvasSize()
globe = createGlobe(canvas, {
devicePixelRatio: Math.min(window.devicePixelRatio, 2),
width: width * 2,
height: width * 2,
phi: 0,
theta: 0,
dark: 1,
diffuse: 1.2,
mapSamples: 16000,
mapBrightness: 6,
baseColor: [0.3, 0.3, 0.3],
markerColor: [0.1, 0.8, 1],
glowColor: [1, 1, 1],
markers: [
{ location: [37.7595, -122.4367], size: 0.03 },
{ location: [40.7128, -74.006], size: 0.1 },
{ location: [55.7558, 37.6176], size: 0.08 }
],
scale: 1,
onRender: (state) => {
if (!pointerInteracting) {
if (Math.abs(userVelocity) > minVelocityThreshold) {
userVelocity *= friction
} else {
userVelocity = 0
}
}
const totalVelocity = userVelocity + autoRotationSpeed
phi += totalVelocity
state.phi = phi
state.theta = theta
state.width = getCanvasSize() * 2
state.height = getCanvasSize() * 2
}
})
}
initGlobe()
canvas.addEventListener('pointerdown', (e) => {
pointerInteracting = true
lastPointerX = e.clientX
userVelocity = 0
canvas.style.cursor = 'grabbing'
if (canvas.setPointerCapture) {
canvas.setPointerCapture(e.pointerId)
}
e.preventDefault()
})
canvas.addEventListener('pointerup', (e) => {
pointerInteracting = false
canvas.style.cursor = 'grab'
if (canvas.hasPointerCapture && canvas.hasPointerCapture(e.pointerId)) {
canvas.releasePointerCapture(e.pointerId)
}
})
canvas.addEventListener('pointermove', (e) => {
if (pointerInteracting) {
const deltaX = e.clientX - lastPointerX
userVelocity = deltaX * sensitivity
lastPointerX = e.clientX
e.preventDefault()
}
})
setTimeout(() => {
canvas.style.opacity = '1'
}, 100)
}
waitForCanvas()
</script>
<style>
.globe-container {
position: relative;
width: 100%;
aspect-ratio: 1;
margin: 0 auto;
overflow: visible;
}
.globe-canvas {
width: 100% !important;
height: 100% !important;
cursor: grab;
opacity: 0;
transition: opacity 1s ease;
contain: layout paint size;
user-select: none;
-webkit-user-select: none;
touch-action: pan-x;
}
.globe-canvas:active {
cursor: grabbing;
}
</style>