MediaWiki:Common.js: Unterschied zwischen den Versionen

Keine Bearbeitungszusammenfassung
Markierung: Manuelle Zurücksetzung
Keine Bearbeitungszusammenfassung
Zeile 1.234: Zeile 1.234:
   }
   }


})();
/* ============================================================
  ADOS – Feuerwerk mit Raketen + Explosion (ES5)
  - dauerhaft
  - KEIN Abdunkeln
  - KEIN Aufhellen (kein Weiß-Schleier)
  - Canvas transparent, pro Frame clearRect
  ============================================================ */
(function () {
  'use strict';
  // true = nur 31.12/01.01, false = immer
  var onlyOnNewYears = false;
  function isNewYears() {
    var d = new Date();
    var m = d.getMonth() + 1;
    var day = d.getDate();
    return (m === 12 && day === 31) || (m === 1 && day === 1);
  }
  if (onlyOnNewYears && !isNewYears()) return;
  function createCanvas() {
    var old = document.getElementById('ados-fireworks-canvas');
    if (old && old.parentNode) old.parentNode.removeChild(old);
    var c = document.createElement('canvas');
    c.id = 'ados-fireworks-canvas';
    c.style.position = 'fixed';
    c.style.left = '0';
    c.style.top = '0';
    c.style.width = '100%';
    c.style.height = '100%';
    c.style.pointerEvents = 'none';
    c.style.zIndex = '9999';
    c.style.opacity = '1';
    document.body.appendChild(c);
    return c;
  }
  function fitCanvas(c) {
    var dpr = window.devicePixelRatio || 1;
    c.width = Math.floor(window.innerWidth * dpr);
    c.height = Math.floor(window.innerHeight * dpr);
    var ctx = c.getContext('2d');
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    // Glow-Look für Partikel
    ctx.globalCompositeOperation = 'lighter';
    return ctx;
  }
  function rand(min, max) { return min + Math.random() * (max - min); }
  function clamp(v, a, b) { return Math.max(a, Math.min(b, v)); }
  var canvas, ctx;
  var rockets = [];
  var particles = [];
  var last = 0;
  var running = true;
  function spawnRocket() {
    var w = window.innerWidth;
    var h = window.innerHeight;
    var x = rand(60, w - 60);
    var y = h + rand(20, 120);
    var targetY = rand(h * 0.12, h * 0.50);
    var vy = rand(-14.0, -18.0);
    var vx = rand(-1.8, 1.8);
    var r = Math.floor(rand(220, 255));
    var g = Math.floor(rand(170, 240));
    var b = Math.floor(rand(80, 170));
    rockets.push({
      x: x, y: y,
      vx: vx, vy: vy,
      targetY: targetY,
      age: 0,
      life: rand(2200, 3200),
      r: r, g: g, b: b,
      trail: []
    });
  }
  function explode(x, y) {
    var count = Math.floor(rand(70, 140));
    var i;
    for (i = 0; i < count; i++) {
      var angle = rand(0, Math.PI * 2);
      var speed = rand(2.5, 7.5);
      var rr = Math.floor(rand(120, 255));
      var gg = Math.floor(rand(80, 240));
      var bb = Math.floor(rand(120, 255));
      // Gold-Bias
      if (Math.random() < 0.40) {
        rr = Math.floor(rand(220, 255));
        gg = Math.floor(rand(150, 230));
        bb = Math.floor(rand(30, 120));
      }
      particles.push({
        x: x, y: y,
        vx: Math.cos(angle) * speed,
        vy: Math.sin(angle) * speed,
        age: 0,
        life: rand(900, 1600),
        size: rand(1.8, 3.8),
        r: rr, g: gg, b: bb
      });
    }
  }
  function tick(ts) {
    if (!running) return;
    if (!last) last = ts;
    var dt = ts - last;
    last = ts;
    var w = window.innerWidth;
    var h = window.innerHeight;
    // WICHTIG: transparent löschen (kein Weiß/Schwarz-Schleier)
    ctx.globalCompositeOperation = 'source-over';
    ctx.clearRect(0, 0, w, h);
    ctx.globalCompositeOperation = 'lighter';
    // Rockets
    var i, r;
    for (i = rockets.length - 1; i >= 0; i--) {
      r = rockets[i];
      r.age += dt;
      r.trail.push({ x: r.x, y: r.y });
      if (r.trail.length > 18) r.trail.shift();
      r.vy += 0.010 * (dt / 16);
      r.x += r.vx * (dt / 16);
      r.y += r.vy * (dt / 16);
      if (r.y <= r.targetY) {
        explode(r.x, r.y);
        rockets.splice(i, 1);
        continue;
      }
      if (r.age >= r.life || r.y < -200 || r.x < -200 || r.x > w + 200) {
        rockets.splice(i, 1);
        continue;
      }
      // Trail
      ctx.beginPath();
      ctx.lineWidth = 3.0;
      ctx.strokeStyle = 'rgba(' + r.r + ',' + r.g + ',' + r.b + ',0.35)';
      var t;
      for (t = 0; t < r.trail.length; t++) {
        var pt = r.trail[t];
        if (t === 0) ctx.moveTo(pt.x, pt.y);
        else ctx.lineTo(pt.x, pt.y);
      }
      ctx.stroke();
      // Kopf
      ctx.beginPath();
      ctx.fillStyle = 'rgba(' + r.r + ',' + r.g + ',' + r.b + ',1)';
      ctx.arc(r.x, r.y, 3.0, 0, Math.PI * 2, false);
      ctx.fill();
    }
    // Particles
    var p;
    for (i = particles.length - 1; i >= 0; i--) {
      p = particles[i];
      p.age += dt;
      if (p.age >= p.life) {
        particles.splice(i, 1);
        continue;
      }
      p.vy += 0.020 * (dt / 16);
      p.vx *= Math.pow(0.985, dt / 16);
      p.vy *= Math.pow(0.985, dt / 16);
      p.x += p.vx * (dt / 16);
      p.y += p.vy * (dt / 16);
      var alpha = clamp(1 - (p.age / p.life), 0, 1);
      ctx.beginPath();
      ctx.fillStyle = 'rgba(' + p.r + ',' + p.g + ',' + p.b + ',' + alpha + ')';
      ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2, false);
      ctx.fill();
    }
    requestAnimationFrame(tick);
  }
  function scheduleRockets() {
    function loop() {
      if (!running) return;
      spawnRocket();
      if (Math.random() < 0.55) spawnRocket();
      if (Math.random() < 0.20) spawnRocket();
      setTimeout(loop, Math.floor(rand(650, 1200)));
    }
    loop();
  }
  function init() {
    canvas = createCanvas();
    ctx = fitCanvas(canvas);
    window.addEventListener('resize', function () {
      if (!canvas) return;
      ctx = fitCanvas(canvas);
    });
    scheduleRockets();
    requestAnimationFrame(tick);
  }
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init, false);
  } else {
    init();
  }
})();
})();