nurjns icon

Feuerwerk Website

nurjns | PRO | 01/03/26 10:42:48 AM UTC | 0 ⭐ | 11657 👁️ | Never ⏰ | []
HTML |

13.43 KB

|

None

|

0 👍

/

0 👎

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Feuerwerk Simulator</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background: #020202;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            color: white;
            user-select: none;
        }
 
        canvas {
            display: block;
            cursor: crosshair;
        }
 
        /* Container für alle Controls */
        #ui-container {
            position: absolute;
            bottom: 30px;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 15px;
            z-index: 10;
            width: 90%;
            max-width: 800px;
            transition: opacity 0.5s; /* Für das Ausblenden im Fullscreen */
        }
 
        /* Die Leiste für die Varianten */
        #controls-variants {
            background: rgba(20, 20, 20, 0.8);
            backdrop-filter: blur(10px);
            padding: 15px 25px;
            border-radius: 50px;
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            gap: 10px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.5);
            border: 1px solid rgba(255,255,255,0.1);
        }
 
        /* Controls für Dauerfeuer und Fullscreen */
        #controls-utility {
            display: flex;
            gap: 10px;
        }
 
        .btn {
            background: transparent;
            border: 1px solid rgba(255, 255, 255, 0.3);
            color: #ccc;
            padding: 10px 18px;
            border-radius: 25px;
            cursor: pointer;
            transition: all 0.3s ease;
            font-size: 13px;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 1px;
            white-space: nowrap;
        }
 
        .btn:hover {
            background: rgba(255, 255, 255, 0.1);
            color: white;
            border-color: white;
        }
 
        .btn.active {
            background: white;
            color: black;
            border-color: white;
            box-shadow: 0 0 15px rgba(255, 255, 255, 0.4);
        }
 
        #auto-btn.auto-active {
            background: #4CAF50; /* Grün */
            color: white;
            border-color: #4CAF50;
            box-shadow: 0 0 20px rgba(76, 175, 80, 0.6);
        }
 
        #fullscreen-btn {
            background: #FF9800; /* Orange */
            color: white;
            border: 1px solid #FF9800;
        }
        #fullscreen-btn:hover {
            background: #E68A00;
        }
 
        #instructions {
            position: absolute;
            top: 20px;
            width: 100%;
            text-align: center;
            pointer-events: none;
            opacity: 0.7;
            font-weight: 300;
            text-shadow: 0 2px 5px black;
            transition: opacity 1.5s ease-out; /* Für das Ausfaden */
        }
 
        /* Spezifische Anweisung im Fullscreen Modus */
        .fullscreen-active #instructions {
            opacity: 1;
        }
        
        /* Klasse für das Ausfaden */
        .fade-out {
            opacity: 0 !important;
        }
    </style>
</head>
<body id="body">
 
    <div id="instructions">Klicke zum Zünden oder aktiviere Dauerfeuer!</div>
 
    <div id="ui-container">
        <div id="controls-utility">
            <button id="auto-btn" class="btn" onclick="toggleAutoFire()">Dauerfeuer: AUS</button>
            <button id="fullscreen-btn" class="btn" onclick="toggleFullscreen()">Full-Screen Modus</button>
        </div>
 
        <div id="controls-variants">
            <button class="btn active variant-btn" onclick="setMode('bunt', this)">Bunt</button>
            <button class="btn variant-btn" onclick="setMode('gold', this)">Goldregen</button>
            <button class="btn variant-btn" onclick="setMode('silber', this)">Silber-Blitz</button>
            <button class="btn variant-btn" onclick="setMode('neon', this)">Neon</button>
            <button class="btn variant-btn" onclick="setMode('palme', this)">Palme</button>
            <button class="btn variant-btn" onclick="setMode('geist', this)">Geist</button>
        </div>
    </div>
 
    <canvas id="canvas"></canvas>
 
    <script>
        // --- SETUP ---
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        const body = document.getElementById('body');
        const instructions = document.getElementById('instructions'); 
        let cw = window.innerWidth;
        let ch = window.innerHeight;
        let fadeOutTimer = null;
 
        canvas.width = cw;
        canvas.height = ch;
        window.addEventListener('resize', () => {
            cw = window.innerWidth;
            ch = window.innerHeight;
            canvas.width = cw;
            canvas.height = ch;
        });
 
        // --- ERWEITERTE KONFIGURATION & NEUE PHYSIK ---
        let currentMode = 'bunt';
        let dauerfeuerAktiv = false;
        let dauerfeuerTimer = null;
        let isFullscreen = false;
 
        const modes = {
            // [GEÄNDERT] Decay und Speed reduziert, um länger zu halten
            bunt: { 
                hueMin: 0, hueMax: 360, saturation: 100, 
                friction: 0.94, gravity: 1.2, 
                countMin: 80, countMax: 180, // Zufällige Größe
                decay: 0.008, speed: 5 
            },
            // [GEÄNDERT] Gravity deutlich niedriger für langes Schweben
            gold: { 
                hueMin: 35, hueMax: 45, saturation: 100, 
                friction: 0.97, gravity: 0.5, // Sehr niedrige Schwerkraft
                countMin: 120, countMax: 250, 
                decay: 0.005, speed: 3.5 // Langsamere Ausbreitung
            },
            // [GEÄNDERT] Decay und Friction leicht reduziert
            silber: { 
                hueMin: 0, hueMax: 360, saturation: 0, 
                friction: 0.95, gravity: 1.2, 
                countMin: 60, countMax: 100, 
                decay: 0.015, speed: 8, brightnessMin: 80 
            },
            // [GEÄNDERT] Decay reduziert
            neon: { 
                hueMin: 180, hueMax: 320, saturation: 100, 
                friction: 0.93, gravity: 1, 
                countMin: 50, countMax: 120, 
                decay: 0.015, speed: 10 
            },
            // [GEÄNDERT] Decay reduziert, damit die "Blätter" länger sichtbar fallen
            palme: { 
                hueMin: 20, hueMax: 30, saturation: 90, 
                friction: 0.99, 
                gravity: 2.5, 
                countMin: 100, countMax: 200, 
                decay: 0.008, speed: 4 
            },
            // [GEÄNDERT] Decay stark reduziert, um länger zu verweilen
            geist: { 
                hueMin: 200, hueMax: 260, saturation: 60, 
                friction: 0.96, gravity: 0.5, 
                countMin: 40, countMax: 90, 
                decay: 0.01, speed: 3, brightnessMin: 30 
            }
        };
 
        // --- KLASSEN ---
 
        // 1. Die Rakete
        class Firework {
            constructor(sx, sy, tx, ty) {
                this.x = sx; this.y = sy;
                this.sx = sx; this.sy = sy;
                this.tx = tx; this.ty = ty;
                this.distanceToTarget = calculateDistance(sx, sy, tx, ty);
                this.distanceTraveled = 0;
                this.coordinates = [];
                this.coordinateCount = 3;
                while(this.coordinateCount--) { this.coordinates.push([this.x, this.y]); }
                this.angle = Math.atan2(ty - sy, tx - sx);
                // [GEÄNDERT] Aufstiegsgeschwindigkeit leicht reduziert
                this.speed = 1.8;
                this.acceleration = 1.04; 
                const mode = modes[currentMode];
                this.brightness = Math.random() * 40 + 60;
                this.hue = Math.floor(Math.random() * (mode.hueMax - mode.hueMin + 1)) + mode.hueMin;
                this.saturation = mode.saturation;
            }
 
            update(index) {
                this.coordinates.pop();
                this.coordinates.unshift([this.x, this.y]);
                this.speed *= this.acceleration;
                const vx = Math.cos(this.angle) * this.speed;
                const vy = Math.sin(this.angle) * this.speed;
                this.distanceTraveled = calculateDistance(this.sx, this.sy, this.x + vx, this.y + vy);
                
                if(this.distanceTraveled >= this.distanceToTarget) {
                    createParticles(this.tx, this.ty, this.hue);
                    fireworks.splice(index, 1);
                } else {
                    this.x += vx;
                    this.y += vy;
                }
            }
 
            draw() {
                ctx.beginPath();
                ctx.moveTo(this.coordinates[this.coordinates.length - 1][0], this.coordinates[this.coordinates.length - 1][1]);
                ctx.lineTo(this.x, this.y);
                ctx.strokeStyle = `hsl(${this.hue}, ${this.saturation}%, ${this.brightness}%)`;
                ctx.stroke();
            }
        }
 
        // 2. Die Partikel
        class Particle {
            constructor(x, y, hue) {
                const mode = modes[currentMode];
                this.x = x; this.y = y;
                this.coordinates = [];
                this.coordinateCount = 6;
                while(this.coordinateCount--) { this.coordinates.push([this.x, this.y]); }
                
                this.angle = Math.random() * Math.PI * 2;
                // [GEÄNDERT] Anfangsgeschwindigkeit leicht niedriger
                this.speed = Math.random() * mode.speed + 0.5; 
                this.friction = mode.friction;
                this.gravity = mode.gravity;
                this.hue = Math.floor(Math.random() * (mode.hueMax - mode.hueMin + 1)) + mode.hueMin;
                this.saturation = mode.saturation;
                
                let bMin = mode.brightnessMin !== undefined ? mode.brightnessMin : 50;
                this.brightness = Math.random() * (100 - bMin) + bMin;
                
                this.alpha = 1;
                this.decay = Math.random() * mode.decay + mode.decay/2;
            }
 
            update(index) {
                this.coordinates.pop();
                this.coordinates.unshift([this.x, this.y]);
                this.speed *= this.friction;
                this.x += Math.cos(this.angle) * this.speed;
                this.y += Math.sin(this.angle) * this.speed + this.gravity;
                this.alpha -= this.decay;
                if(this.alpha <= this.decay) { particles.splice(index, 1); }
            }
 
            draw() {
                ctx.beginPath();
                ctx.moveTo(this.coordinates[this.coordinates.length - 1][0], this.coordinates[this.coordinates.length - 1][1]);
                ctx.lineTo(this.x, this.y);
                ctx.strokeStyle = `hsla(${this.hue}, ${this.saturation}%, ${this.brightness}%, ${this.alpha})`;
                ctx.stroke();
            }
        }
 
        // --- ENGINE LOGIK ---
        const fireworks = [];
        const particles = [];
 
        function calculateDistance(p1x, p1y, p2x, p2y) {
            const xDistance = p1x - p2x;
            const yDistance = p1y - p2y;
            return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));
        }
 
        // [GEÄNDERT] Erzeugt eine zufällige Anzahl von Partikeln (klein bis groß)
        function createParticles(x, y, hue) {
            const mode = modes[currentMode];
            
            // Zufällige Partikelanzahl innerhalb des definierten Min/Max-Bereichs
            const count = Math.floor(Math.random() * (mode.countMax - mode.countMin + 1)) + mode.countMin;
            
            for(let i = 0; i < count; i++) {
                particles.push(new Particle(x, y, hue));
            }
        }
 
        function loop() {
            requestAnimationFrame(loop);
            ctx.globalCompositeOperation = 'destination-out';
            ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; 
            ctx.fillRect(0, 0, cw, ch);
            ctx.globalCompositeOperation = 'lighter';
 
            let i = fireworks.length;
            while(i--) { fireworks[i].draw(); fireworks[i].update(i); }
            let j = particles.length;
            while(j--) { particles[j].draw(); particles[j].update(j); }
        }
 
        // --- INTERAKTION & MODI (Full-Screen Fade-Out Logik beibehalten) ---
        
        window.setMode = function(modeName, btnElement) {
            currentMode = modeName;
            document.querySelectorAll('.variant-btn').forEach(btn => btn.classList.remove('active'));
            btnElement.classList.add('active');
        };
 
        window.toggleAutoFire = function() {
            dauerfeuerAktiv = !dauerfeuerAktiv;
            updateAutoFireUI();
            if(dauerfeuerAktiv) {
                autoFireLoop();
            } else {
                clearTimeout(dauerfeuerTimer);
            }
        };
 
        function updateAutoFireUI() {
             const btn = document.getElementById('auto-btn');
             if(dauerfeuerAktiv) {
                btn.textContent = "Dauerfeuer: AN";
                btn.classList.add('auto-active');
            } else {
                btn.textContent = "Dauerfeuer: AUS";
                btn.classList.remove('auto-active');
            }
        }
 
        function autoFireLoop() {
            if(!dauerfeuerAktiv) return;
 
            let targetX = Math.random() * cw;
            let targetY = Math.random() * (ch * 0.6);
            let startX = Math.random() * cw;
 
            fireworks.push(new Firework(startX, ch, targetX, targetY));
 
            let randomDelay = Math.random() * 900 + 300;
            dauerfeuerTimer = setTimeout(autoFireLoop, randomDelay);
        }
 
        // --- FULLSCREEN LOGIK (mit 3s Fade-Out) ---
 
        window.toggleFullscreen = function() {
            if (!isFullscreen) {
                enterFullscreen();
            } else {
                exitFullscreen();
            }
        };
 
        function enterFullscreen() {
            isFullscreen = true;
            document.getElementById('ui-container').style.opacity = '0';
            body.classList.add('fullscreen-active');
            instructions.textContent = "Klicke zum Beenden";
            instructions.classList.remove('fade-out'); 
            
            // Timer starten, um den Text nach 3 Sekunden auszublenden
            fadeOutTimer = setTimeout(() => {
                instructions.classList.add('fade-out');
            }, 3000); 
            
            if (!dauerfeuerAktiv) {
                toggleAutoFire();
            }
            
            const elem = document.documentElement;
            if (elem.requestFullscreen) {
                elem.requestFullscreen();
            } else if (elem.webkitRequestFullscreen) {
                elem.webkitRequestFullscreen();
            }
            
            canvas.removeEventListener('mousedown', handleManualClick);
            canvas.addEventListener('mousedown', exitFullscreen, { once: true });
        }
 
        function exitFullscreen() {
            clearTimeout(fadeOutTimer); 
            instructions.classList.remove('fade-out'); 
            
            isFullscreen = false;
            document.getElementById('ui-container').style.opacity = '1';
            body.classList.remove('fullscreen-active');
            document.getElementById('instructions').textContent = "Klicke zum Zünden oder aktiviere Dauerfeuer!";
 
            if (dauerfeuerAktiv) {
                 toggleAutoFire();
            }
 
            if (document.exitFullscreen) {
                document.exitFullscreen();
            } else if (document.webkitExitFullscreen) {
                document.webkitExitFullscreen();
            }
            
            canvas.removeEventListener('mousedown', exitFullscreen);
            canvas.addEventListener('mousedown', handleManualClick);
        }
        
        document.addEventListener('fullscreenchange', function() {
            if (!document.fullscreenElement && isFullscreen) {
                exitFullscreen();
            }
        });
        document.addEventListener('webkitfullscreenchange', function() {
            if (!document.webkitFullscreenElement && isFullscreen) {
                exitFullscreen();
            }
        });
 
        function handleManualClick(e) {
             e.preventDefault();
             fireworks.push(new Firework(cw / 2, ch, e.clientX, e.clientY));
        }
 
        canvas.addEventListener('mousedown', handleManualClick);
 
        loop();
 
    </script>
</body>
</html>

Comments