script.js

22.55 KB
12/11/2025 13:25
JS
script.js
// ============================================
// FUTURISTIC CHRISTMAS - LEAFBOX TECHNOLOGIES
// JavaScript Interactions & Animations
// ============================================

// ========== CONFIGURATION ==========
const CONFIG = {
    snow: {
        count: 100,
        maxSize: 4,
        minSize: 1,
        speed: 0.5
    },
    tree: {
        particleCount: 150,
        glowIntensity: 20
    },
    sound: {
        enabled: false,
        volume: 0.3
    }
};

// ========== PARTICLE SNOW ANIMATION ==========
class SnowCanvas {
    constructor(canvasId) {
        this.canvas = document.getElementById(canvasId);
        this.ctx = this.canvas.getContext('2d');
        this.particles = [];
        this.init();
        this.animate();
    }

    init() {
        this.resize();
        window.addEventListener('resize', () => this.resize());
        this.createParticles();
    }

    resize() {
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;
    }

    createParticles() {
        this.particles = [];
        const particleCount = window.innerWidth < 768 ? 50 : CONFIG.snow.count;
        
        for (let i = 0; i < particleCount; i++) {
            this.particles.push({
                x: Math.random() * this.canvas.width,
                y: Math.random() * this.canvas.height,
                size: Math.random() * (CONFIG.snow.maxSize - CONFIG.snow.minSize) + CONFIG.snow.minSize,
                speedY: Math.random() * CONFIG.snow.speed + 0.5,
                speedX: Math.random() * 0.5 - 0.25,
                opacity: Math.random() * 0.5 + 0.3,
                color: this.getSnowColor()
            });
        }
    }

    getSnowColor() {
        const colors = ['#00E5FF', '#00FF99', '#FFFFFF'];
        return colors[Math.floor(Math.random() * colors.length)];
    }

    animate() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

        this.particles.forEach(particle => {
            // Draw particle
            this.ctx.beginPath();
            this.ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
            this.ctx.fillStyle = particle.color;
            this.ctx.globalAlpha = particle.opacity;
            this.ctx.fill();

            // Add glow effect
            const gradient = this.ctx.createRadialGradient(
                particle.x, particle.y, 0,
                particle.x, particle.y, particle.size * 2
            );
            gradient.addColorStop(0, particle.color);
            gradient.addColorStop(1, 'transparent');
            this.ctx.fillStyle = gradient;
            this.ctx.globalAlpha = particle.opacity * 0.5;
            this.ctx.fill();

            // Update position
            particle.y += particle.speedY;
            particle.x += particle.speedX;

            // Reset particle when it goes off screen
            if (particle.y > this.canvas.height) {
                particle.y = -10;
                particle.x = Math.random() * this.canvas.width;
            }
            if (particle.x > this.canvas.width || particle.x < 0) {
                particle.x = Math.random() * this.canvas.width;
            }
        });

        this.ctx.globalAlpha = 1;
        requestAnimationFrame(() => this.animate());
    }
}

// ========== DIGITAL CHRISTMAS TREE ==========
class DigitalTree {
    constructor(canvasId) {
        this.canvas = document.getElementById(canvasId);
        this.ctx = this.canvas.getContext('2d');
        this.particles = [];
        this.lines = [];
        this.animationProgress = 0;
        this.init();
    }

    init() {
        // Set canvas size
        const size = window.innerWidth < 768 ? 300 : 400;
        this.canvas.width = size;
        this.canvas.height = size;
        
        this.createTreeStructure();
        this.animate();
    }

    createTreeStructure() {
        const centerX = this.canvas.width / 2;
        const baseY = this.canvas.height - 50;
        const topY = 30;
        const maxWidth = this.canvas.width * 0.6;

        // Create tree outline with particles
        for (let i = 0; i < CONFIG.tree.particleCount; i++) {
            const progress = i / CONFIG.tree.particleCount;
            const y = baseY - (baseY - topY) * progress;
            const width = maxWidth * (1 - progress);
            const x = centerX + (Math.random() - 0.5) * width;

            this.particles.push({
                x: x,
                y: y,
                targetX: x,
                targetY: y,
                currentX: centerX,
                currentY: baseY,
                size: Math.random() * 3 + 1,
                color: this.getTreeColor(),
                delay: Math.random() * 50,
                glowSize: Math.random() * 10 + 5
            });
        }

        // Create connecting lines
        for (let i = 0; i < this.particles.length - 1; i++) {
            if (Math.random() > 0.7) {
                this.lines.push({
                    start: this.particles[i],
                    end: this.particles[i + 1]
                });
            }
        }

        // Add star at top
        this.star = {
            x: centerX,
            y: topY - 20,
            size: 15,
            points: 5,
            rotation: 0
        };

        // Add trunk
        this.trunk = {
            x: centerX,
            y: baseY,
            width: 20,
            height: 40
        };
    }

    getTreeColor() {
        const colors = ['#00FF99', '#00E5FF', '#FFD700'];
        return colors[Math.floor(Math.random() * colors.length)];
    }

    drawStar(x, y, size, rotation) {
        this.ctx.save();
        this.ctx.translate(x, y);
        this.ctx.rotate(rotation);
        this.ctx.beginPath();

        for (let i = 0; i < 5; i++) {
            const angle = (Math.PI * 2 * i) / 5 - Math.PI / 2;
            const x = Math.cos(angle) * size;
            const y = Math.sin(angle) * size;
            
            if (i === 0) {
                this.ctx.moveTo(x, y);
            } else {
                this.ctx.lineTo(x, y);
            }

            const innerAngle = angle + Math.PI / 5;
            const innerX = Math.cos(innerAngle) * (size * 0.4);
            const innerY = Math.sin(innerAngle) * (size * 0.4);
            this.ctx.lineTo(innerX, innerY);
        }

        this.ctx.closePath();
        this.ctx.fillStyle = '#FFD700';
        this.ctx.fill();

        // Add glow
        this.ctx.shadowBlur = 20;
        this.ctx.shadowColor = '#FFD700';
        this.ctx.fill();

        this.ctx.restore();
    }

    animate() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

        // Update animation progress
        if (this.animationProgress < 1) {
            this.animationProgress += 0.01;
        }

        // Draw connecting lines
        this.lines.forEach(line => {
            const startProgress = Math.min(1, this.animationProgress * 2);
            const endProgress = Math.min(1, (this.animationProgress - 0.3) * 2);

            if (startProgress > line.start.delay / 100) {
                this.ctx.beginPath();
                this.ctx.moveTo(line.start.currentX, line.start.currentY);
                this.ctx.lineTo(line.end.currentX, line.end.currentY);
                this.ctx.strokeStyle = 'rgba(0, 255, 153, 0.3)';
                this.ctx.lineWidth = 1;
                this.ctx.stroke();
            }
        });

        // Draw and update particles
        this.particles.forEach(particle => {
            if (this.animationProgress > particle.delay / 100) {
                // Animate to target position
                const progress = Math.min(1, (this.animationProgress - particle.delay / 100) * 2);
                particle.currentX = particle.currentX + (particle.targetX - particle.currentX) * progress * 0.1;
                particle.currentY = particle.currentY + (particle.targetY - particle.currentY) * progress * 0.1;

                // Draw particle glow
                const gradient = this.ctx.createRadialGradient(
                    particle.currentX, particle.currentY, 0,
                    particle.currentX, particle.currentY, particle.glowSize
                );
                gradient.addColorStop(0, particle.color);
                gradient.addColorStop(1, 'transparent');
                this.ctx.fillStyle = gradient;
                this.ctx.globalAlpha = 0.6;
                this.ctx.fillRect(
                    particle.currentX - particle.glowSize,
                    particle.currentY - particle.glowSize,
                    particle.glowSize * 2,
                    particle.glowSize * 2
                );

                // Draw particle
                this.ctx.globalAlpha = 1;
                this.ctx.beginPath();
                this.ctx.arc(particle.currentX, particle.currentY, particle.size, 0, Math.PI * 2);
                this.ctx.fillStyle = particle.color;
                this.ctx.fill();
            }
        });

        // Draw trunk
        this.ctx.fillStyle = '#374151';
        this.ctx.fillRect(
            this.trunk.x - this.trunk.width / 2,
            this.trunk.y,
            this.trunk.width,
            this.trunk.height
        );

        // Draw star
        if (this.animationProgress > 0.8) {
            this.star.rotation += 0.02;
            this.drawStar(this.star.x, this.star.y, this.star.size, this.star.rotation);
        }

        requestAnimationFrame(() => this.animate());
    }
}

// ========== COUNTDOWN TIMER ==========
class CountdownTimer {
    constructor() {
        this.targetDate = new Date('2026-01-01T00:00:00').getTime();
        this.elements = {
            days: document.getElementById('days'),
            hours: document.getElementById('hours'),
            minutes: document.getElementById('minutes'),
            seconds: document.getElementById('seconds')
        };
        this.update();
        setInterval(() => this.update(), 1000);
    }

    update() {
        const now = new Date().getTime();
        const distance = this.targetDate - now;

        if (distance < 0) {
            this.displayTime(0, 0, 0, 0);
            return;
        }

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

        this.displayTime(days, hours, minutes, seconds);
    }

    displayTime(days, hours, minutes, seconds) {
        this.elements.days.textContent = String(days).padStart(2, '0');
        this.elements.hours.textContent = String(hours).padStart(2, '0');
        this.elements.minutes.textContent = String(minutes).padStart(2, '0');
        this.elements.seconds.textContent = String(seconds).padStart(2, '0');
    }
}

// ========== THEME TOGGLE ==========
class ThemeToggle {
    constructor() {
        this.button = document.getElementById('themeToggle');
        this.currentTheme = localStorage.getItem('theme') || 'dark';
        this.init();
    }

    init() {
        this.applyTheme(this.currentTheme);
        this.button.addEventListener('click', () => this.toggle());
    }

    toggle() {
        this.currentTheme = this.currentTheme === 'dark' ? 'light' : 'dark';
        this.applyTheme(this.currentTheme);
        localStorage.setItem('theme', this.currentTheme);
    }

    applyTheme(theme) {
        if (theme === 'light') {
            document.body.classList.add('light-theme');
        } else {
            document.body.classList.remove('light-theme');
        }
    }
}

// ========== SOUND TOGGLE ==========
class SoundToggle {
    constructor() {
        this.button = document.getElementById('soundToggle');
        this.audio = null;
        this.isPlaying = false;
        this.init();
    }

    init() {
        this.button.addEventListener('click', () => this.toggle());
        
        // Create audio context (simple chime sound using Web Audio API)
        this.createAudioContext();
    }

    createAudioContext() {
        try {
            this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
        } catch (e) {
            console.log('Web Audio API not supported');
        }
    }

    playChime() {
        if (!this.audioContext) return;

        const now = this.audioContext.currentTime;
        const oscillator = this.audioContext.createOscillator();
        const gainNode = this.audioContext.createGain();

        oscillator.connect(gainNode);
        gainNode.connect(this.audioContext.destination);

        // Christmas bell-like sound
        oscillator.frequency.setValueAtTime(800, now);
        oscillator.frequency.exponentialRampToValueAtTime(400, now + 0.1);

        gainNode.gain.setValueAtTime(0.3, now);
        gainNode.gain.exponentialRampToValueAtTime(0.01, now + 0.5);

        oscillator.start(now);
        oscillator.stop(now + 0.5);
    }

    toggle() {
        this.isPlaying = !this.isPlaying;
        
        if (this.isPlaying) {
            this.button.classList.remove('muted');
            this.playChime();
            // Play chime periodically
            this.interval = setInterval(() => this.playChime(), 5000);
        } else {
            this.button.classList.add('muted');
            if (this.interval) {
                clearInterval(this.interval);
            }
        }
    }
}

// ========== EASTER EGG ==========
class EasterEgg {
    constructor() {
        this.modal = document.getElementById('easterEggModal');
        this.closeBtn = document.getElementById('modalClose');
        this.trigger = document.getElementById('easterEgg');
        this.privacyLink = document.getElementById('privacyLink');
        this.clickCount = 0;
        this.init();
    }

    init() {
        // Easter egg on cybersecurity card
        this.trigger.addEventListener('click', (e) => {
            this.clickCount++;
            if (this.clickCount === 3) {
                this.show();
                this.clickCount = 0;
            }
        });

        // Easter egg on privacy link
        this.privacyLink.addEventListener('click', (e) => {
            e.preventDefault();
            this.show();
        });

        this.closeBtn.addEventListener('click', () => this.hide());
        
        this.modal.addEventListener('click', (e) => {
            if (e.target === this.modal) {
                this.hide();
            }
        });

        // Keyboard shortcut: Ctrl+Shift+X
        document.addEventListener('keydown', (e) => {
            if (e.ctrlKey && e.shiftKey && e.key === 'X') {
                this.show();
            }
        });
    }

    show() {
        this.modal.classList.add('active');
        document.body.style.overflow = 'hidden';
    }

    hide() {
        this.modal.classList.remove('active');
        document.body.style.overflow = '';
    }
}

// ========== SMOOTH SCROLL ==========
function initSmoothScroll() {
    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener('click', function (e) {
            const href = this.getAttribute('href');
            if (href === '#' || href === '#!') return;
            
            e.preventDefault();
            const target = document.querySelector(href);
            
            if (target) {
                target.scrollIntoView({
                    behavior: 'smooth',
                    block: 'start'
                });
            }
        });
    });
}

// ========== SCROLL ANIMATIONS ==========
class ScrollAnimations {
    constructor() {
        this.elements = document.querySelectorAll('.timeline-item, .service-card');
        this.init();
    }

    init() {
        this.observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    entry.target.style.opacity = '1';
                    entry.target.style.transform = 'translateY(0)';
                }
            });
        }, {
            threshold: 0.1
        });

        this.elements.forEach(el => {
            this.observer.observe(el);
        });
    }
}

// ========== INTERACTIVE PARTICLES ON HOVER ==========
class InteractiveParticles {
    constructor() {
        this.init();
    }

    init() {
        const serviceCards = document.querySelectorAll('.service-card');
        
        serviceCards.forEach(card => {
            card.addEventListener('mouseenter', (e) => {
                this.createParticleBurst(e.currentTarget);
            });
        });
    }

    createParticleBurst(element) {
        const rect = element.getBoundingClientRect();
        const particles = 10;

        for (let i = 0; i < particles; i++) {
            const particle = document.createElement('div');
            particle.style.position = 'fixed';
            particle.style.left = rect.left + rect.width / 2 + 'px';
            particle.style.top = rect.top + rect.height / 2 + 'px';
            particle.style.width = '4px';
            particle.style.height = '4px';
            particle.style.borderRadius = '50%';
            particle.style.background = 'var(--leaf-green)';
            particle.style.pointerEvents = 'none';
            particle.style.zIndex = '9999';
            particle.style.boxShadow = '0 0 10px var(--leaf-green)';

            document.body.appendChild(particle);

            const angle = (Math.PI * 2 * i) / particles;
            const velocity = 100;
            const vx = Math.cos(angle) * velocity;
            const vy = Math.sin(angle) * velocity;

            let x = 0, y = 0;
            const animate = () => {
                x += vx * 0.01;
                y += vy * 0.01;
                
                particle.style.transform = `translate(${x}px, ${y}px)`;
                particle.style.opacity = Math.max(0, 1 - Math.sqrt(x * x + y * y) / 100);

                if (Math.sqrt(x * x + y * y) < 100) {
                    requestAnimationFrame(animate);
                } else {
                    particle.remove();
                }
            };

            animate();
        }
    }
}

// ========== CONTACT BUTTON INTERACTION ==========
function initContactButton() {
    const contactButton = document.getElementById('contactButton');
    
    contactButton.addEventListener('click', () => {
        // Create a glowing pulse effect
        const pulse = document.createElement('div');
        pulse.style.position = 'fixed';
        pulse.style.left = '50%';
        pulse.style.top = '50%';
        pulse.style.transform = 'translate(-50%, -50%)';
        pulse.style.width = '0px';
        pulse.style.height = '0px';
        pulse.style.borderRadius = '50%';
        pulse.style.border = '2px solid var(--leaf-green)';
        pulse.style.pointerEvents = 'none';
        pulse.style.zIndex = '9999';
        
        document.body.appendChild(pulse);

        let size = 0;
        const animate = () => {
            size += 20;
            pulse.style.width = size + 'px';
            pulse.style.height = size + 'px';
            pulse.style.opacity = Math.max(0, 1 - size / 500);

            if (size < 500) {
                requestAnimationFrame(animate);
            } else {
                pulse.remove();
            }
        };

        animate();

        // Show alert
        setTimeout(() => {
            alert('🎄 Thank you for your interest! Our team will contact you soon.\n\nEmail: hello@leafbox.tech\nPhone: +1 (555) 123-4567');
        }, 300);
    });
}

// ========== CURSOR TRAIL EFFECT ==========
class CursorTrail {
    constructor() {
        this.trail = [];
        this.maxTrail = 20;
        this.init();
    }

    init() {
        if (window.innerWidth < 768) return; // Disable on mobile

        document.addEventListener('mousemove', (e) => {
            this.trail.push({
                x: e.clientX,
                y: e.clientY,
                timestamp: Date.now()
            });

            if (this.trail.length > this.maxTrail) {
                this.trail.shift();
            }

            this.render();
        });
    }

    render() {
        // Remove old trail elements
        document.querySelectorAll('.cursor-trail').forEach(el => {
            if (Date.now() - parseInt(el.dataset.timestamp) > 500) {
                el.remove();
            }
        });

        // Create new trail element
        if (this.trail.length > 0) {
            const last = this.trail[this.trail.length - 1];
            const trail = document.createElement('div');
            trail.className = 'cursor-trail';
            trail.style.position = 'fixed';
            trail.style.left = last.x + 'px';
            trail.style.top = last.y + 'px';
            trail.style.width = '6px';
            trail.style.height = '6px';
            trail.style.borderRadius = '50%';
            trail.style.background = 'var(--ice-blue)';
            trail.style.pointerEvents = 'none';
            trail.style.zIndex = '9998';
            trail.style.boxShadow = '0 0 10px var(--ice-blue)';
            trail.style.opacity = '0.6';
            trail.style.transform = 'translate(-50%, -50%)';
            trail.style.transition = 'opacity 0.5s ease-out';
            trail.dataset.timestamp = last.timestamp;

            document.body.appendChild(trail);

            setTimeout(() => {
                trail.style.opacity = '0';
            }, 10);
        }
    }
}

// ========== INITIALIZE ALL ==========
document.addEventListener('DOMContentLoaded', () => {
    console.log('🎄 LeafBox Technologies - Merry Christmas! 🎄');
    console.log('Where technology meets the magic of Christmas ✨');
    
    // Initialize all components
    const snowCanvas = new SnowCanvas('snowCanvas');
    const digitalTree = new DigitalTree('treeCanvas');
    const countdown = new CountdownTimer();
    const themeToggle = new ThemeToggle();
    const soundToggle = new SoundToggle();
    const easterEgg = new EasterEgg();
    const scrollAnimations = new ScrollAnimations();
    const interactiveParticles = new InteractiveParticles();
    const cursorTrail = new CursorTrail();
    
    initSmoothScroll();
    initContactButton();

    // Log easter egg hint
    console.log('💡 Hint: Try clicking the Cybersecurity card 3 times... 🎁');
    console.log('💡 Or use the keyboard shortcut: Ctrl+Shift+X');
});

// ========== PERFORMANCE OPTIMIZATION ==========
// Reduce animations on low-end devices
if (navigator.hardwareConcurrency < 4) {
    CONFIG.snow.count = 50;
    CONFIG.tree.particleCount = 100;
}

// Pause animations when tab is not visible
document.addEventListener('visibilitychange', () => {
    if (document.hidden) {
        // Pause non-essential animations
        console.log('Tab hidden - pausing animations');
    } else {
        console.log('Tab visible - resuming animations');
    }
});