| |
|
|
| class CelestialSystem { |
| constructor() { |
| this.isPlaying = true; |
| this.timeScale = 10; |
| this.currentTime = 6 * 60; |
| this.dayDuration = 24 * 60; |
| |
| this.initializeElements(); |
| this.startSimulation(); |
| this.setupEventListeners(); |
| } |
|
|
| initializeElements() { |
| this.sun = document.getElementById('sun'); |
| this.moon = document.getElementById('moon'); |
| this.stars = document.getElementById('stars'); |
| this.currentTimeDisplay = document.getElementById('currentTime'); |
| this.timeScaleInput = document.getElementById('timeScale'); |
| this.timeScaleValue = document.getElementById('timeScaleValue'); |
| this.pauseBtn = document.getElementById('pauseBtn'); |
| this.sunPosition = document.getElementById('sunPosition'); |
| this.moonPosition = document.getElementById('moonPosition'); |
| this.timeProgress = document.getElementById('timeProgress'); |
| } |
|
|
| setupEventListeners() { |
| this.timeScaleInput.addEventListener('input', (e) => { |
| this.timeScale = parseInt(e.target.value); |
| this.timeScaleValue.textContent = `${this.timeScale}x`; |
| this.updateSimulationSpeed(); |
| this.updateCelestialInfo(); |
| this.updateBackground(); |
| this.updateAtmosphericEffects(); |
| this.updateLighting(); |
| this.updateTimeDisplay(); |
| this.updateProgress(); |
| this.updateStars(); |
| this.updateHorizon(); |
| this.updateClouds(); |
| this.updateShadows(); |
| this.updateReflections(); |
| this.updateParticles(); |
| this.updateAudio(); |
| this.updateWeather(); |
| this.updateSeasonalEffects(); |
| }); |
|
|
| this.pauseBtn.addEventListener('click', () => { |
| this.togglePause(); |
| }); |
|
|
| |
| document.addEventListener('keydown', (e) => { |
| if (e.code === 'Space') { |
| e.preventDefault(); |
| this.togglePause(); |
| } |
| }); |
| } |
|
|
| togglePause() { |
| this.isPlaying = !this.isPlaying; |
| const icon = this.pauseBtn.querySelector('i'); |
| const text = this.pauseBtn.querySelector('span:last-child'); |
| |
| if (this.isPlaying) { |
| icon.setAttribute('data-feather', 'pause'); |
| text.textContent = 'Pause'; |
| this.pauseBtn.classList.remove('bg-red-600'); |
| this.pauseBtn.classList.add('bg-primary-600'); |
| } else { |
| icon.setAttribute('data-feather', 'play'); |
| text.textContent = 'Play'; |
| this.pauseBtn.classList.remove('bg-primary-600'); |
| this.pauseBtn.classList.add('bg-red-600'); |
| } |
| feather.replace(); |
| } |
|
|
| startSimulation() { |
| const update = () => { |
| if (this.isPlaying) { |
| this.currentTime += this.timeScale / 60; |
| if (this.currentTime >= this.dayDuration) { |
| this.currentTime = 0; |
| } |
| this.updateAll(); |
| } |
| requestAnimationFrame(update); |
| }; |
| update(); |
| } |
|
|
| updateAll() { |
| this.updateCelestialPositions(); |
| this.updateBackground(); |
| this.updateAtmosphericEffects(); |
| this.updateLighting(); |
| this.updateTimeDisplay(); |
| this.updateCelestialInfo(); |
| this.updateProgress(); |
| this.updateStars(); |
| this.updateHorizon(); |
| this.updateClouds(); |
| this.updateShadows(); |
| this.updateReflections(); |
| this.updateParticles(); |
| this.updateAudio(); |
| this.updateWeather(); |
| this.updateSeasonalEffects(); |
| } |
|
|
| updateCelestialPositions() { |
| const timeRatio = this.currentTime / this.dayDuration; |
| |
| |
| const sunX = 10 + (timeRatio * 80); |
| const sunY = this.calculateSunElevation(timeRatio); |
| |
| |
| const moonX = 90 - (timeRatio * 80); |
| const moonY = this.calculateMoonElevation(timeRatio); |
| |
| this.sun.style.left = `${sunX}%`; |
| this.sun.style.bottom = `${sunY}%`; |
| |
| this.moon.style.left = `${moonX}%`; |
| this.moon.style.bottom = `${moonY}%`; |
| |
| |
| const sunOpacity = this.calculateSunOpacity(timeRatio); |
| const moonOpacity = this.calculateMoonOpacity(timeRatio); |
| |
| this.sun.style.opacity = sunOpacity; |
| this.moon.style.opacity = moonOpacity; |
| } |
|
|
| calculateSunElevation(timeRatio) { |
| |
| const angle = (timeRatio - 0.5) * Math.PI; |
| return 5 + (Math.cos(angle) * 25); |
| } |
|
|
| calculateMoonElevation(timeRatio) { |
| |
| const angle = ((timeRatio + 0.5) % 1 - 0.5) * Math.PI; |
| return 5 + (Math.cos(angle) * 20); |
| } |
|
|
| calculateSunOpacity(timeRatio) { |
| |
| if (timeRatio >= 0.25 && timeRatio <= 0.75) { |
| return 1; |
| } |
| |
| if (timeRatio >= 0.2 && timeRatio < 0.25) { |
| return (timeRatio - 0.2) / 0.05; |
| } |
| if (timeRatio > 0.75 && timeRatio <= 0.8) { |
| return (0.8 - timeRatio) / 0.05; |
| } |
| return 0; |
| } |
|
|
| calculateMoonOpacity(timeRatio) { |
| |
| if (timeRatio <= 0.2 || timeRatio >= 0.8) { |
| return 1; |
| } |
| |
| if (timeRatio > 0.75 && timeRatio < 0.8) { |
| return (timeRatio - 0.75) / 0.05; |
| } |
| if (timeRatio > 0.2 && timeRatio < 0.25) { |
| return (0.25 - timeRatio) / 0.05; |
| } |
| return 0; |
| } |
|
|
| updateBackground() { |
| const timeRatio = this.currentTime / this.dayDuration; |
| const container = this.sun.parentElement; |
| |
| |
| let gradient; |
| if (timeRatio < 0.25) { |
| gradient = 'from-indigo-900 via-purple-700 to-blue-500'; |
| } else if (timeRatio < 0.35) { |
| gradient = 'from-blue-500 via-cyan-400 to-blue-300'; |
| } else if (timeRatio < 0.65) { |
| gradient = 'from-blue-400 via-cyan-300 to-blue-200'; |
| } else if (timeRatio < 0.75) { |
| gradient = 'from-orange-400 via-red-500 to-purple-700'; |
| } else { |
| gradient = 'from-purple-700 via-indigo-800 to-gray-900'; |
| } |
| |
| container.className = container.className.replace(/from-\S+ via-\S+ to-\S+/, gradient); |
| } |
|
|
| updateAtmosphericEffects() { |
| const timeRatio = this.currentTime / this.dayDuration; |
| |
| |
| const sunGlow = this.sun.querySelector('div'); |
| if (timeRatio >= 0.35 && timeRatio <= 0.65) { |
| sunGlow.classList.add('animate-pulse'); |
| } else { |
| sunGlow.classList.remove('animate-pulse'); |
| } |
| } |
|
|
| updateLighting() { |
| const timeRatio = this.currentTime / this.dayDuration; |
| |
| |
| document.documentElement.style.setProperty('--light-intensity', this.calculateLightIntensity(timeRatio)); |
| } |
|
|
| calculateLightIntensity(timeRatio) { |
| |
| const center = 0.5; |
| const width = 0.25; |
| const distance = Math.abs(timeRatio - center); |
| return Math.max(0, 1 - (distance / width) ** 2); |
| } |
|
|
| updateTimeDisplay() { |
| const hours = Math.floor(this.currentTime / 60); |
| const minutes = Math.floor(this.currentTime % 60); |
| const period = hours >= 12 ? 'PM' : 'AM'; |
| const displayHours = hours % 12 || 12; |
| |
| this.currentTimeDisplay.textContent = |
| `${displayHours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')} ${period}`; |
| } |
|
|
| updateCelestialInfo() { |
| const timeRatio = this.currentTime / this.dayDuration; |
| |
| |
| const sunElevation = Math.round(this.calculateSunElevation(timeRatio) * 3.6); |
| const sunAzimuth = Math.round(90 + (timeRatio * 180) % 360); |
| this.sunPosition.textContent = `Elevation: ${sunElevation}° | Azimuth: ${sunAzimuth}°`; |
| |
| |
| const moonElevation = Math.round(this.calculateMoonElevation(timeRatio) * 3.6); |
| const moonAzimuth = Math.round(270 - (timeRatio * 180) % 360); |
| this.moonPosition.textContent = `Elevation: ${moonElevation}° | Azimuth: ${moonAzimuth}°`; |
| } |
|
|
| updateProgress() { |
| const dayProgress = Math.min(1, Math.max(0, (this.currentTime / this.dayDuration - 0.25) / 0.5); |
| const nightProgress = 1 - dayProgress; |
| |
| this.timeProgress.textContent = |
| `Day: ${Math.round(dayProgress * 100)}% | Night: ${Math.round(nightProgress * 100)}%`; |
| } |
|
|
| updateStars() { |
| const timeRatio = this.currentTime / this.dayDuration; |
| const starOpacity = (timeRatio <= 0.2 || timeRatio >= 0.8) ? 1 : 0; |
| this.stars.style.opacity = starOpacity; |
| } |
|
|
| updateHorizon() { |
| |
| const timeRatio = this.currentTime / this.dayDuration; |
| const horizon = document.querySelector('.absolute.bottom-0'); |
| |
| if (timeRatio < 0.25 || timeRatio > 0.75) { |
| horizon.className = horizon.className.replace(/from-\S+ to-\S+/, 'from-gray-900 to-gray-700'); |
| } else { |
| horizon.className = horizon.className.replace(/from-\S+ to-\S+/, 'from-green-900 to-green-700'); |
| } |
|
|
| updateClouds() { |
| |
| const timeRatio = this.currentTime / this.dayDuration; |
| document.documentElement.style.setProperty('--cloud-brightness', this.calculateCloudBrightness(timeRatio)); |
| } |
|
|
| calculateCloudBrightness(timeRatio) { |
| return Math.max(0.3, Math.min(1, this.calculateLightIntensity(timeRatio) + 0.3)); |
| } |
|
|
| updateShadows() { |
| const timeRatio = this.currentTime / this.dayDuration; |
| const shadowLength = 50 + (Math.abs(timeRatio - 0.5) * 100); |
| document.documentElement.style.setProperty('--shadow-length', `${shadowLength}px`); |
| } |
|
|
| updateReflections() { |
| |
| const timeRatio = this.currentTime / this.dayDuration; |
| const reflectionIntensity = timeRatio >= 0.35 && timeRatio <= 0.65 ? 0.8 : 0.3; |
| document.documentElement.style.setProperty('--reflection-intensity', reflectionIntensity); |
| } |
|
|
| updateParticles() { |
| |
| const timeRatio = this.currentTime / this.dayDuration; |
| const particleDensity = timeRatio < 0.25 || timeRatio > 0.75 ? 0.7 : 0.1); |
| document.documentElement.style.setProperty('--particle-density', particleDensity); |
| } |
|
|
| updateAudio() { |
| |
| const timeRatio = this.currentTime / this.dayDuration; |
| const audioVolume = Math.min(1, Math.max(0.1, this.calculateLightIntensity(timeRatio))); |
| document.documentElement.style.setProperty('--audio-volume', audioVolume); |
| } |
|
|
| updateWeather() { |
| |
| const timeRatio = this.currentTime / this.dayDuration; |
| document.documentElement.style.setProperty('--weather-intensity', Math.random())); |
| } |
|
|
| updateSeasonalEffects() { |
| |
| const timeRatio = this.currentTime / this.dayDuration; |
| document.documentElement.style.setProperty('--season-factor', 1); |
| } |
|
|
| updateSimulationSpeed() { |
| |
| document.documentElement.style.setProperty('--animation-speed', `${this.timeScale / 10}s`); |
| } |
| } |
|
|
| |
| document.addEventListener('DOMContentLoaded', () => { |
| new CelestialSystem(); |
| |
| |
| const sections = document.querySelectorAll('section'); |
| sections.forEach((section, index) => { |
| section.classList.add('fade-in-up'); |
| section.style.animationDelay = `${index * 0.2}s`; |
| }); |
| }); |
|
|
| |
| function smoothTransition(element, property, targetValue, duration = 1000) { |
| element.style.transition = `${property} ${duration}ms cubic-bezier(0.4, 0, 0.2, 1)'; |
| element.style[property] = targetValue; |
| } |
| |
| // Export for potential module usage |
| if (typeof module !== 'undefined' && module.exports) { |
| module.exports = CelestialSystem; |
| } |