Files
3d-homepage-zozomu/main.js
2026-02-20 21:08:02 +00:00

187 lines
7.6 KiB
JavaScript

/**
* Lumerel — 3D Homepage
* main.js
*
* Handles:
* - Spline 3D scene loading & pointer-events toggle
* - Navbar scroll behaviour (frosted glass on scroll)
* - Video autoplay fallback (iOS / autoplay-blocked browsers)
* - Waitlist button click handlers
* - Global helpers exposed on window.Lumerel
*/
/* ── Spline Integration ────────────────────────────────────────────────
*
* WHEN YOUR SPLINE SCENE IS READY:
*
* Option A — HTML (simplest):
* Open index.html, find #spline-layer, delete .spline-placeholder,
* and paste in:
*
* <spline-viewer
* url="https://prod.spline.design/YOUR_SCENE_ID/scene.splinecode"
* events-target="global"
* ></spline-viewer>
*
* Option B — JavaScript:
* Call window.Lumerel.loadSplineScene('https://prod.spline.design/…');
* This removes the placeholder and injects <spline-viewer> for you.
*
* Option C — @splinetool/runtime (advanced):
* import { Application } from '@splinetool/runtime';
* const app = new Application(canvas);
* app.load('https://prod.spline.design/YOUR_SCENE_ID/scene.splinecode');
*
* For interactive scenes (mouse/touch on the 3D layer), call:
* window.Lumerel.enableSpline()
*
* ────────────────────────────────────────────────────────────────────── */
const splineLayer = document.getElementById('spline-layer');
/**
* Enable pointer events on the 3D layer so Spline scenes
* can receive mouse/touch input.
* Call this AFTER your Spline scene has loaded.
*/
function enableSpline() {
if (splineLayer) {
splineLayer.style.pointerEvents = 'auto';
console.log('[Lumerel] Spline pointer events ENABLED.');
}
}
/**
* Disable pointer events on the 3D layer (default state).
* Ensures all UI elements (buttons, links) remain fully clickable.
*/
function disableSpline() {
if (splineLayer) {
splineLayer.style.pointerEvents = 'none';
console.log('[Lumerel] Spline pointer events DISABLED.');
}
}
/**
* Programmatically load a Spline scene by URL.
* Removes the placeholder orbs and injects a <spline-viewer>.
* @param {string} sceneUrl - The Spline scene URL (.splinecode)
* @param {boolean} [enableInteraction=false] - Auto-enable pointer events
*/
function loadSplineScene(sceneUrl, enableInteraction = false) {
if (!splineLayer) return;
// Remove placeholder
const placeholder = splineLayer.querySelector('.spline-placeholder');
if (placeholder) placeholder.remove();
// Inject spline-viewer
const viewer = document.createElement('spline-viewer');
viewer.setAttribute('url', sceneUrl);
viewer.setAttribute('events-target', 'global');
viewer.style.width = '100%';
viewer.style.height = '100%';
viewer.style.display = 'block';
viewer.addEventListener('load', () => {
console.log('[Lumerel] Spline scene loaded:', sceneUrl);
if (enableInteraction) enableSpline();
});
splineLayer.appendChild(viewer);
console.log('[Lumerel] Spline scene injected:', sceneUrl);
}
// Listen for spline-viewer ready event if already in HTML
if (splineLayer) {
const existingViewer = splineLayer.querySelector('spline-viewer');
if (existingViewer) {
existingViewer.addEventListener('load', () => {
console.log('[Lumerel] Spline scene loaded.');
// Uncomment to auto-enable 3D interactivity:
// enableSpline();
});
}
}
/* ── Navbar Scroll Effect ───────────────────────────────────────────────
Adds a frosted-glass background when the user scrolls past 24px.
─────────────────────────────────────────────────────────────────────── */
const navbar = document.getElementById('navbar');
function handleNavbarScroll() {
if (!navbar) return;
if (window.scrollY > 24) {
navbar.style.background = 'rgba(0, 0, 0, 0.45)';
navbar.style.backdropFilter = 'blur(16px)';
navbar.style.webkitBackdropFilter = 'blur(16px)';
} else {
navbar.style.background = 'transparent';
navbar.style.backdropFilter = 'none';
navbar.style.webkitBackdropFilter = 'none';
}
}
window.addEventListener('scroll', handleNavbarScroll, { passive: true });
handleNavbarScroll(); // Run on load
/* ── Video Autoplay Fallback ────────────────────────────────────────────
Some browsers block autoplay even with muted. This retries on user
interaction if the video fails to start automatically.
─────────────────────────────────────────────────────────────────────── */
const heroVideo = document.querySelector('.hero__video');
if (heroVideo) {
const playVideo = () => {
heroVideo.play().catch(() => {
// Autoplay still blocked — video stays paused, page still works
});
};
heroVideo.addEventListener('canplaythrough', playVideo, { once: true });
// Retry on first user gesture (for strict autoplay policies)
const retryOnInteraction = () => {
playVideo();
document.removeEventListener('click', retryOnInteraction);
document.removeEventListener('touchstart', retryOnInteraction);
};
if (heroVideo.paused) {
document.addEventListener('click', retryOnInteraction, { once: true });
document.addEventListener('touchstart', retryOnInteraction, { once: true });
}
}
/* ── Waitlist Button Handlers ───────────────────────────────────────────
Replace the console.log with your modal, form, or redirect logic.
─────────────────────────────────────────────────────────────────────── */
function handleWaitlistClick(source) {
console.log(`[Lumerel] Waitlist CTA clicked — source: ${source}`);
// TODO: Open modal, redirect, or trigger waitlist form
// Example: window.location.href = 'https://waitlist.lumerel.com';
// Example: document.getElementById('waitlist-modal').showModal();
}
const navbarCta = document.getElementById('navbar-cta');
const heroCta = document.getElementById('hero-cta');
if (navbarCta) navbarCta.addEventListener('click', () => handleWaitlistClick('navbar'));
if (heroCta) heroCta.addEventListener('click', () => handleWaitlistClick('hero'));
/* ── Global API ─────────────────────────────────────────────────────────
Expose helpers so you can call them from the browser console
or from other scripts after the page loads.
─────────────────────────────────────────────────────────────────────── */
window.Lumerel = {
loadSplineScene,
enableSpline,
disableSpline,
};
console.log(
'%c LUMEREL ',
'background:#000;color:#fff;font-weight:700;font-size:13px;padding:4px 8px;border:1px solid rgba(255,255,255,0.3);border-radius:4px;',
'— 3D Homepage ready. window.Lumerel API available.'
);