diff --git a/.gitignore b/.gitignore index 53e30e3..6b78d50 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ spectra-lab-local.png +spectra-lab-redesign.png .DS_Store diff --git a/app.js b/app.js index ede35ae..9520e09 100644 --- a/app.js +++ b/app.js @@ -1,168 +1,43 @@ -const pointerGlow = document.getElementById('pointerGlow'); -const themeToggle = document.getElementById('themeToggle'); -const themeLabel = document.getElementById('themeLabel'); -const sceneCards = document.querySelectorAll('.scene-card'); -const counters = document.querySelectorAll('[data-counter]'); -const sceneDetailTitle = document.getElementById('sceneDetailTitle'); -const sceneDetailCopy = document.getElementById('sceneDetailCopy'); -const particleCanvas = document.getElementById('particleCanvas'); +const modeToggle = document.getElementById('modeToggle'); +const modeLabel = document.getElementById('modeLabel'); +const workItems = document.querySelectorAll('.work-item'); +const workIndex = document.getElementById('workIndex'); +const workTitle = document.getElementById('workTitle'); +const workCopy = document.getElementById('workCopy'); +const revealBlocks = document.querySelectorAll('.reveal'); -const themes = [ - { className: '', label: 'Aurora' }, - { className: 'theme-ember', label: 'Ember' }, - { className: 'theme-tide', label: 'Tide' }, -]; - -let themeIndex = 0; - -function setTheme(index) { - document.body.classList.remove('theme-ember', 'theme-tide'); - const theme = themes[index]; - if (theme.className) { - document.body.classList.add(theme.className); - } - themeLabel.textContent = theme.label; +function applyMode() { + const isLight = document.body.classList.contains('light-mode'); + modeToggle.textContent = isLight ? 'Dark Mode' : 'Light Mode'; + modeLabel.textContent = isLight ? 'Editorial Light' : 'Editorial Dark'; } -function animateCounter(element) { - const target = Number(element.dataset.counter || 0); - const duration = 1400; - const start = performance.now(); - - function step(now) { - const progress = Math.min((now - start) / duration, 1); - const eased = 1 - Math.pow(1 - progress, 3); - element.textContent = Math.round(target * eased); - if (progress < 1) requestAnimationFrame(step); - } - - requestAnimationFrame(step); -} - -function mountParticleField() { - if (!particleCanvas) return; - const ctx = particleCanvas.getContext('2d'); - if (!ctx) return; - - const particles = []; - const particleCount = 48; - let width = 0; - let height = 0; - - function resize() { - width = window.innerWidth; - height = window.innerHeight; - particleCanvas.width = width * window.devicePixelRatio; - particleCanvas.height = height * window.devicePixelRatio; - particleCanvas.style.width = `${width}px`; - particleCanvas.style.height = `${height}px`; - ctx.setTransform(window.devicePixelRatio, 0, 0, window.devicePixelRatio, 0, 0); - } - - function seed() { - particles.length = 0; - for (let i = 0; i < particleCount; i += 1) { - particles.push({ - x: Math.random() * width, - y: Math.random() * height, - r: Math.random() * 1.8 + 0.8, - vx: (Math.random() - 0.5) * 0.28, - vy: (Math.random() - 0.5) * 0.28, - }); - } - } - - function draw() { - ctx.clearRect(0, 0, width, height); - - particles.forEach((particle, index) => { - particle.x += particle.vx; - particle.y += particle.vy; - - if (particle.x < -20) particle.x = width + 20; - if (particle.x > width + 20) particle.x = -20; - if (particle.y < -20) particle.y = height + 20; - if (particle.y > height + 20) particle.y = -20; - - ctx.beginPath(); - ctx.fillStyle = 'rgba(255,255,255,0.38)'; - ctx.arc(particle.x, particle.y, particle.r, 0, Math.PI * 2); - ctx.fill(); - - for (let j = index + 1; j < particles.length; j += 1) { - const other = particles[j]; - const dx = particle.x - other.x; - const dy = particle.y - other.y; - const distance = Math.sqrt(dx * dx + dy * dy); - if (distance < 130) { - ctx.beginPath(); - ctx.strokeStyle = `rgba(130, 180, 255, ${0.12 * (1 - distance / 130)})`; - ctx.moveTo(particle.x, particle.y); - ctx.lineTo(other.x, other.y); - ctx.stroke(); - } - } - }); - - requestAnimationFrame(draw); - } - - resize(); - seed(); - draw(); - window.addEventListener('resize', () => { - resize(); - seed(); - }); -} - -window.addEventListener('pointermove', (event) => { - pointerGlow.style.transform = `translate(${event.clientX}px, ${event.clientY}px)`; +modeToggle?.addEventListener('click', () => { + document.body.classList.toggle('light-mode'); + applyMode(); }); -sceneCards.forEach((card) => { - card.addEventListener('pointermove', (event) => { - const rect = card.getBoundingClientRect(); - const x = event.clientX - rect.left; - const y = event.clientY - rect.top; - const rotateX = ((y / rect.height) - 0.5) * -8; - const rotateY = ((x / rect.width) - 0.5) * 10; - - card.style.setProperty('--mx', `${(x / rect.width) * 100}%`); - card.style.setProperty('--my', `${(y / rect.height) * 100}%`); - card.style.transform = `translateY(-6px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`; +workItems.forEach((item) => { + item.addEventListener('click', () => { + workItems.forEach((button) => button.classList.remove('is-active')); + item.classList.add('is-active'); + workIndex.textContent = item.dataset.index || ''; + workTitle.textContent = item.dataset.title || ''; + workCopy.textContent = item.dataset.copy || ''; }); - - card.addEventListener('pointerleave', () => { - card.style.transform = ''; - }); - - card.addEventListener('click', () => { - sceneCards.forEach((item) => item.classList.remove('is-active')); - card.classList.add('is-active'); - sceneDetailTitle.textContent = card.dataset.sceneTitle || ''; - sceneDetailCopy.textContent = card.dataset.sceneCopy || ''; - }); -}); - -themeToggle.addEventListener('click', () => { - themeIndex = (themeIndex + 1) % themes.length; - setTheme(themeIndex); }); const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { - animateCounter(entry.target); + entry.target.classList.add('is-visible'); observer.unobserve(entry.target); } }); }, - { threshold: 0.4 }, + { threshold: 0.18 }, ); -counters.forEach((counter) => observer.observe(counter)); -sceneCards[0]?.classList.add('is-active'); -setTheme(themeIndex); -mountParticleField(); +revealBlocks.forEach((block) => observer.observe(block)); +applyMode(); diff --git a/index.html b/index.html index d20afe4..b406f4d 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,7 @@