Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!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>
Advertisement