MediaWiki:Common.js: Unterschied zwischen den Versionen
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung Markierung: Manuelle Zurücksetzung |
Admin (Diskussion | Beiträge) 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(); | |||
} | |||
})(); | })(); | ||