countdown.js

10.30 KB
12/11/2025 13:10
JS
countdown.js
/**
 * Countdown Timer for LeafBox Technologies Website
 * Creates an animated countdown to New Year
 */

class CountdownTimer {
  constructor() {
    this.targetDate = this.getNewYearDate();
    this.elements = {
      days: document.getElementById('days'),
      hours: document.getElementById('hours'),
      minutes: document.getElementById('minutes'),
      seconds: document.getElementById('seconds')
    };

    this.interval = null;
    this.isActive = false;

    this.init();
  }

  init() {
    if (this.hasValidElements()) {
      this.start();
    } else {
      console.warn('Countdown elements not found');
    }
  }

  hasValidElements() {
    return this.elements.days &&
      this.elements.hours &&
      this.elements.minutes &&
      this.elements.seconds;
  }

  getNewYearDate() {
    const now = new Date();
    const currentYear = now.getFullYear();
    const nextYear = currentYear + 1;

    // New Year is January 1st of next year at 00:00:00
    return new Date(nextYear, 0, 1, 0, 0, 0, 0);
  }

  start() {
    if (this.isActive) return;

    this.isActive = true;
    this.updateCountdown(); // Initial update
    this.interval = setInterval(() => this.updateCountdown(), 1000);
  }

  stop() {
    if (!this.isActive) return;

    this.isActive = false;
    clearInterval(this.interval);
    this.interval = null;
  }

  updateCountdown() {
    const now = new Date().getTime();
    const timeLeft = this.targetDate.getTime() - now;

    if (timeLeft <= 0) {
      this.handleCountdownComplete();
      return;
    }

    const timeComponents = this.calculateTimeComponents(timeLeft);
    this.displayTime(timeComponents);
    this.addAnimationEffects(timeComponents);
  }

  calculateTimeComponents(timeLeft) {
    const days = Math.floor(timeLeft / (1000 * 60 * 60 * 24));
    const hours = Math.floor((timeLeft % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
    const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
    const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);

    return {days, hours, minutes, seconds};
  }

  displayTime({days, hours, minutes, seconds}) {
    this.updateElement(this.elements.days, this.padNumber(days));
    this.updateElement(this.elements.hours, this.padNumber(hours));
    this.updateElement(this.elements.minutes, this.padNumber(minutes));
    this.updateElement(this.elements.seconds, this.padNumber(seconds));
  }

  updateElement(element, value) {
    if (!element) return;

    const currentValue = element.textContent;
    if (currentValue !== value) {
      // Add flash effect when number changes
      element.parentElement.classList.add('number-change');

      element.textContent = value;

      // Remove flash effect after animation
      setTimeout(() => {
        element.parentElement.classList.remove('number-change');
      }, 500);
    }
  }

  padNumber(number) {
    return number.toString().padStart(2, '0');
  }

  addAnimationEffects({days, hours, minutes, seconds}) {
    // Add special effects for certain values
    if (seconds === 0) {
      // New minute reached
      this.addMinuteEffect();
    }

    if (minutes === 0 && seconds === 0) {
      // New hour reached
      this.addHourEffect();
    }

    if (hours === 0 && minutes === 0 && seconds === 0) {
      // New day reached
      this.addDayEffect();
    }

    // Add glow effect when we're close to New Year
    const daysUntilNewYear = Math.floor((this.targetDate.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24));
    if (daysUntilNewYear <= 7) {
      this.addNearNewYearEffect();
    }
  }

  addMinuteEffect() {
    const countdownContainer = document.querySelector('.countdown');
    if (countdownContainer) {
      countdownContainer.style.animation = 'none';
      setTimeout(() => {
        countdownContainer.style.animation = 'pulse 0.5s ease-in-out';
      }, 10);

      setTimeout(() => {
        countdownContainer.style.animation = '';
      }, 500);
    }
  }

  addHourEffect() {
    // Add a more pronounced effect for hours
    const countdownNumbers = document.querySelectorAll('.countdown-number');
    countdownNumbers.forEach(number => {
      number.style.animation = 'bounce 0.6s ease-in-out';
      setTimeout(() => {
        number.style.animation = '';
      }, 600);
    });
  }

  addDayEffect() {
    // Add special effect for days
    const daysElement = this.elements.days;
    if (daysElement) {
      daysElement.style.color = '#B89B5F';
      daysElement.style.textShadow = '0 0 10px #B89B5F';

      setTimeout(() => {
        daysElement.style.color = '';
        daysElement.style.textShadow = '';
      }, 2000);
    }
  }

  addNearNewYearEffect() {
    const countdownItems = document.querySelectorAll('.countdown-item');
    countdownItems.forEach(item => {
      item.style.border = '1px solid #B89B5F';
      item.style.boxShadow = '0 0 15px rgba(184, 155, 95, 0.3)';
    });
  }

  handleCountdownComplete() {
    this.stop();
    this.displayNewYearMessage();
    this.addCelebrationEffect();
  }

  displayNewYearMessage() {
    const countdownContainer = document.querySelector('.countdown-container');
    if (countdownContainer) {
      const message = document.createElement('div');
      message.className = 'new-year-message';
      message.innerHTML = `
                <h3 style="color: #B89B5F; margin: 16px 0 8px; font-size: 24px;">
                    🎉 Happy New Year! 🎉
                </h3>
                <p style="color: #6B7280; font-size: 16px;">
                    Wishing you innovation and success in ${this.targetDate.getFullYear()}!
                </p>
            `;

      countdownContainer.appendChild(message);

      // Remove countdown
      const countdown = document.getElementById('countdown');
      if (countdown) {
        countdown.style.display = 'none';
      }
    }
  }

  addCelebrationEffect() {
    // Add confetti effect (simplified version)
    this.createConfetti();

    // Update page title temporarily
    const originalTitle = document.title;
    document.title = '🎊 Happy New Year - LeafBox Technologies! 🎊';

    setTimeout(() => {
      document.title = originalTitle;
    }, 10000);
  }

  createConfetti() {
    const confettiContainer = document.createElement('div');
    confettiContainer.style.position = 'fixed';
    confettiContainer.style.top = '0';
    confettiContainer.style.left = '0';
    confettiContainer.style.width = '100%';
    confettiContainer.style.height = '100%';
    confettiContainer.style.pointerEvents = 'none';
    confettiContainer.style.zIndex = '9999';
    document.body.appendChild(confettiContainer);

    // Create confetti pieces
    for (let i = 0; i < 50; i++) {
      const confetti = document.createElement('div');
      confetti.style.position = 'absolute';
      confetti.style.width = '10px';
      confetti.style.height = '10px';
      confetti.style.backgroundColor = this.getRandomColor();
      confetti.style.left = Math.random() * 100 + '%';
      confetti.style.top = '-10px';
      confetti.style.borderRadius = '50%';
      confetti.style.animation = `confettiFall ${Math.random() * 3 + 2}s linear forwards`;

      confettiContainer.appendChild(confetti);
    }

    // Add confetti animation
    if (!document.getElementById('confetti-styles')) {
      const style = document.createElement('style');
      style.id = 'confetti-styles';
      style.textContent = `
                @keyframes confettiFall {
                    to {
                        transform: translateY(100vh) rotate(360deg);
                        opacity: 0;
                    }
                }
            `;
      document.head.appendChild(style);
    }

    // Remove confetti after animation
    setTimeout(() => {
      confettiContainer.remove();
    }, 5000);
  }

  getRandomColor() {
    const colors = ['#B89B5F', '#2A4B3A', '#FDFDFC', '#5F87B8'];
    return colors[Math.floor(Math.random() * colors.length)];
  }

  // Public methods
  pause() {
    this.stop();
  }

  resume() {
    this.start();
  }

  setTargetDate(date) {
    this.targetDate = date;
    if (this.isActive) {
      this.stop();
      this.start();
    }
  }

  destroy() {
    this.stop();

    // Clean up DOM
    const newYearMessage = document.querySelector('.new-year-message');
    if (newYearMessage) {
      newYearMessage.remove();
    }

    const countdown = document.getElementById('countdown');
    if (countdown) {
      countdown.style.display = '';
    }
  }

  // Utility method to format time for display
  formatTime(ms) {
    const components = this.calculateTimeComponents(ms);
    return `${this.padNumber(components.days)}d ${this.padNumber(components.hours)}h ${this.padNumber(components.minutes)}m ${this.padNumber(components.seconds)}s`;
  }

  // Get time remaining
  getTimeRemaining() {
    const now = new Date().getTime();
    const timeLeft = this.targetDate.getTime() - now;
    return Math.max(0, timeLeft);
  }

  // Check if countdown is complete
  isComplete() {
    return this.getTimeRemaining() <= 0;
  }
}

// Initialize countdown when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
  // Check if we're close to New Year
  const now = new Date();
  const newYear = new Date(now.getFullYear() + 1, 0, 1);
  const daysUntilNewYear = Math.floor((newYear.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));

  // Only show countdown if we're within 60 days of New Year
  if (daysUntilNewYear <= 60) {
    const countdownTimer = new CountdownTimer();

    // Make it globally accessible for debugging
    window.countdownTimer = countdownTimer;

    // Add keyboard controls for accessibility
    document.addEventListener('keydown', (e) => {
      if (e.key === ' ') { // Spacebar
        e.preventDefault();
        if (countdownTimer.isActive) {
          countdownTimer.pause();
        } else {
          countdownTimer.resume();
        }
      }
    });

    // Handle visibility change for performance
    document.addEventListener('visibilitychange', () => {
      if (document.hidden) {
        countdownTimer.pause();
      } else {
        countdownTimer.resume();
      }
    });
  } else {
    // Hide countdown if we're too far from New Year
    const countdownContainer = document.querySelector('.countdown-container');
    if (countdownContainer) {
      countdownContainer.style.display = 'none';
    }
  }
});

// Export for module systems
if (typeof module !== 'undefined' && module.exports) {
  module.exports = CountdownTimer;
}