-
Dome Gallery Animation
HTML
CSS
JavaScript
Drag to spin the world
:root { --radius: 320px; --size: 170px; /* Large enough to overlap and hide the core */ } @media (max-width: 768px) { :root { --radius: 170px; --size: 100px; } } body { margin: 0; /* Space background */ background: radial-gradient(circle at center, #0a1128 0%, #000 100%); height: 100vh; display: flex; justify-content: center; align-items: center; overflow: hidden; touch-action: none; cursor: grab; color: white; font-family: 'Segoe UI', sans-serif; } .stage { perspective: 1500px; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; position: relative; } /* Atmospheric Halo */ .atmosphere { position: absolute; width: calc(var(--radius) * 2.3); height: calc(var(--radius) * 2.3); border-radius: 50%; background: radial-gradient(circle, transparent 65%, rgba(0, 150, 255, 0.15) 85%, transparent 100%); pointer-events: none; z-index: 0; } .globe { position: relative; width: var(--size); height: var(--size); transform-style: preserve-3d; will-change: transform; /* The Ocean Core */ background: radial-gradient(circle, #1e5799 0%, #000033 80%); border-radius: 50%; box-shadow: 0 0 40px rgba(0, 100, 255, 0.3); } .item { position: absolute; width: 100%; height: 100%; border-radius: 50%; /* Circle format */ overflow: hidden; backface-visibility: hidden; border: 1px solid rgba(255, 255, 255, 0.15); /* Positioning logic */ transform: rotateY(var(--lon)) rotateX(var(--lat)) translateZ(var(--radius)); } .item img { width: 105%; /* Slight overlap to prevent cracks */ height: 105%; object-fit: cover; filter: brightness(0.8) contrast(1.1); transition: transform 0.4s ease, filter 0.4s ease; } .item:hover img { filter: brightness(1.2) contrast(1); transform: scale(1.1); } .stage:active { cursor: grabbing; } .label { position: absolute; bottom: 30px; font-size: 12px; letter-spacing: 3px; text-transform: uppercase; opacity: 0.5; pointer-events: none; }
const globe = document.getElementById('globe'); const stage = document.getElementById('stage'); // Spherical distribution for 32 images const layout = [ { count: 4, lat: 65 }, // Top { count: 7, lat: 35 }, // North { count: 10, lat: 0 }, // Equator { count: 7, lat: -35 }, // South { count: 4, lat: -65 } // Bottom ]; let imgIndex = 1; layout.forEach(tier => { for (let i = 0; i < tier.count; i++) { const lon = (360 / tier.count) * i; const item = document.createElement('div'); item.className = 'item'; item.style.setProperty('--lat', `${tier.lat}deg`); item.style.setProperty('--lon', `${lon}deg`); const img = document.createElement('img'); // Unique random images img.src = `https://picsum.photos/400/400?v=${imgIndex++}`; item.appendChild(img); globe.appendChild(item); } }); // Interaction state let isDragging = false; let startX, startY; let rotX = -15; let rotY = 0; let autoSpin = 0; // Pointer Events for Mouse & Touch stage.addEventListener('pointerdown', (e) => { isDragging = true; startX = e.clientX; startY = e.clientY; globe.style.transition = 'none'; stage.setPointerCapture(e.pointerId); }); window.addEventListener('pointermove', (e) => { if (!isDragging) return; const dx = (e.clientX - startX) * 0.2; const dy = (e.clientY - startY) * 0.2; // Temporary rotation update globe.style.transform = `rotateX(${rotX - dy}deg) rotateY(${rotY + dx + autoSpin}deg)`; }); window.addEventListener('pointerup', (e) => { if (!isDragging) return; isDragging = false; rotY += (e.clientX - startX) * 0.2; rotX -= (e.clientY - startY) * 0.2; globe.style.transition = 'transform 0.8s cubic-bezier(0.2, 1, 0.3, 1)'; }); // Loop for gentle auto-rotation function animate() { if (!isDragging) { autoSpin += 0.1; // Adjust speed here globe.style.transform = `rotateX(${rotX}deg) rotateY(${rotY + autoSpin}deg)`; } requestAnimationFrame(animate); } animate();
Live Preview