162 lines
6.2 KiB
JavaScript
162 lines
6.2 KiB
JavaScript
/**
|
|
* Lumerel — 3D Homepage · main.js
|
|
*
|
|
* Responsibilities:
|
|
* 1. Spline 3D scene pointer-events toggle + runtime loader
|
|
* 2. Navbar scroll backdrop blur
|
|
* 3. Background video autoplay fallback (iOS Safari)
|
|
* 4. Waitlist button click hooks
|
|
* 5. Public API exposed on window.Lumerel
|
|
*/
|
|
|
|
/* ── 1. Spline / 3D Layer Integration ─────────────────────────────
|
|
*
|
|
* When your Spline scene is ready, choose one of three methods:
|
|
*
|
|
* ── Method A (HTML — recommended) ─────────────────────────────────
|
|
* Open index.html → find #spline-layer → delete .spline-placeholder
|
|
* → paste in:
|
|
*
|
|
* <spline-viewer
|
|
* url="https://prod.spline.design/YOUR_SCENE_ID/scene.splinecode"
|
|
* events-target="global"
|
|
* ></spline-viewer>
|
|
*
|
|
* ── Method B (JavaScript runtime) ─────────────────────────────────
|
|
* Call from console or your code:
|
|
* window.Lumerel.loadSplineScene('https://prod.spline.design/…')
|
|
*
|
|
* ── Method C (Interactive scenes) ─────────────────────────────────
|
|
* After scene loads, enable pointer events:
|
|
* window.Lumerel.enableSpline()
|
|
*
|
|
* The <spline-viewer> web component is already loaded via CDN
|
|
* in the <head> of index.html — no extra imports needed.
|
|
*
|
|
─────────────────────────────────────────────────────────────────── */
|
|
|
|
const splineLayer = document.getElementById('spline-layer');
|
|
|
|
/** Allow pointer events on the 3D layer (for interactive Spline scenes). */
|
|
function enableSpline() {
|
|
if (splineLayer) splineLayer.style.pointerEvents = 'auto';
|
|
}
|
|
|
|
/** Block pointer events on the 3D layer (default — keeps UI clickable). */
|
|
function disableSpline() {
|
|
if (splineLayer) splineLayer.style.pointerEvents = 'none';
|
|
}
|
|
|
|
/**
|
|
* Programmatically load a Spline scene URL at runtime.
|
|
* Removes the placeholder orbs and inserts a <spline-viewer>.
|
|
* @param {string} url - The full Spline scene URL
|
|
* @param {boolean} [interactive=false] - Enable pointer events for interactive scenes
|
|
*/
|
|
function loadSplineScene(url, interactive = false) {
|
|
if (!splineLayer) return;
|
|
|
|
// Remove placeholder if present
|
|
const placeholder = splineLayer.querySelector('.spline-placeholder');
|
|
if (placeholder) placeholder.remove();
|
|
|
|
// Create and configure the viewer
|
|
const viewer = document.createElement('spline-viewer');
|
|
viewer.setAttribute('url', url);
|
|
viewer.setAttribute('events-target', 'global');
|
|
viewer.style.cssText = 'width:100%;height:100%;display:block;';
|
|
|
|
viewer.addEventListener('load', () => {
|
|
console.log('[Lumerel] ✓ Spline scene loaded:', url);
|
|
if (interactive) enableSpline();
|
|
});
|
|
|
|
splineLayer.appendChild(viewer);
|
|
console.log('[Lumerel] Loading Spline scene…');
|
|
}
|
|
|
|
// Auto-detect if a <spline-viewer> was placed directly in index.html
|
|
if (splineLayer) {
|
|
const viewer = splineLayer.querySelector('spline-viewer');
|
|
if (viewer) {
|
|
viewer.addEventListener('load', () => {
|
|
console.log('[Lumerel] ✓ Spline scene loaded.');
|
|
// Uncomment to enable 3D mouse interaction after scene loads:
|
|
// enableSpline();
|
|
});
|
|
}
|
|
}
|
|
|
|
/* ── 2. Navbar Scroll Backdrop ─────────────────────────────────────── */
|
|
const navbar = document.getElementById('navbar');
|
|
|
|
function updateNavbar() {
|
|
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', updateNavbar, { passive: true });
|
|
updateNavbar(); // run once on load
|
|
|
|
/* ── 3. Background Video Fallback ─────────────────────────────────── */
|
|
const heroVideo = document.querySelector('.hero__video');
|
|
|
|
if (heroVideo) {
|
|
// Gradient fallback if video fails to load
|
|
heroVideo.addEventListener('error', () => {
|
|
console.warn('[Lumerel] Background video failed — using gradient fallback.');
|
|
const wrap = heroVideo.closest('.hero__video-wrap');
|
|
if (wrap) {
|
|
heroVideo.style.display = 'none';
|
|
wrap.style.background = 'linear-gradient(135deg, #0a0a0a 0%, #111 50%, #0a0a0a 100%)';
|
|
}
|
|
});
|
|
|
|
// iOS Safari: force play on user interaction if autoplay was blocked
|
|
const tryPlay = () => {
|
|
if (heroVideo.paused) {
|
|
heroVideo.play().catch(() => {});
|
|
}
|
|
document.removeEventListener('touchstart', tryPlay);
|
|
document.removeEventListener('click', tryPlay);
|
|
};
|
|
|
|
heroVideo.play().catch(() => {
|
|
document.addEventListener('touchstart', tryPlay, { once: true, passive: true });
|
|
document.addEventListener('click', tryPlay, { once: true });
|
|
});
|
|
}
|
|
|
|
/* ── 4. Waitlist Button Hooks ─────────────────────────────────────── */
|
|
const waitlistBtns = document.querySelectorAll('.btn-pill');
|
|
|
|
waitlistBtns.forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
// Hook your waitlist modal / form here
|
|
console.log('[Lumerel] Waitlist button clicked — connect your form/modal here.');
|
|
// Example: document.getElementById('waitlist-modal').showModal();
|
|
});
|
|
});
|
|
|
|
/* ── 5. Public API ────────────────────────────────────────────────── */
|
|
window.Lumerel = {
|
|
/** Load a Spline scene at runtime. */
|
|
loadSplineScene,
|
|
/** Enable pointer events on the 3D layer (for interactive scenes). */
|
|
enableSpline,
|
|
/** Disable pointer events on the 3D layer (default state). */
|
|
disableSpline,
|
|
/** Direct reference to the 3D layer element. */
|
|
splineLayer,
|
|
};
|
|
|
|
console.log('[Lumerel] ✓ Ready. window.Lumerel API available.');
|