<script>
(function () {
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
canvas.style.cssText =
'position:fixed;top:0;left:0;pointer-events:none;z-index:9999;';
const ctx = canvas.getContext('2d');
let W, H;
let mx = -200, my = -200;
let prevMx = -200, prevMy = -200;
let particles = [];
let idleFrame = 0;
const IDLE_INTERVAL = 28;
const COLORS = [
'#FF69B4', '#FF1493', '#FFB6C1', '#FF85C2',
'#FFA0D0', '#FFC0CB', '#FF82AB', '#FFAAD4',
'#E75480', '#FFD1DC', '#FFE4E1', '#FF6EB4'
];
const GOLD = ['#FFD700', '#FFF0A0', '#FFFACD'];
// --- Canvas-Größe ---
function resize() {
canvas.width = W = window.innerWidth;
canvas.height = H = window.innerHeight;
}
resize();
window.addEventListener('resize', resize);
// --- Maus global verfolgen ---
document.addEventListener('mousemove', function (e) {
mx = e.clientX;
my = e.clientY;
});
// --- Partikel erzeugen ---
function spawnOne(x, y, tiny) {
const angle = Math.random() * Math.PI * 2;
const speed = tiny
? 0.1 + Math.random() * 0.3
: 0.4 + Math.random() * 1.2;
const isGold = Math.random() < 0.2;
particles.push({
x: x, y: y,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed - (tiny ? 0.15 : 0.5),
size: tiny ? 1 + Math.random() * 1.5 : 2 + Math.random() * 4,
color: isGold
? GOLD[Math.floor(Math.random() * GOLD.length)]
: COLORS[Math.floor(Math.random() * COLORS.length)],
life: 1,
decay: tiny
? 0.006 + Math.random() * 0.008
: 0.012 + Math.random() * 0.018,
twinkle: Math.random() * Math.PI * 2,
shape: Math.random() > 0.5 ? 'star' : 'circle'
});
}
function spawnParticles(x, y, moving) {
if (moving) {
const count = 4 + Math.floor(Math.random() * 4);
for (let i = 0; i < count; i++) spawnOne(x, y, false);
} else {
spawnOne(x, y, true);
}
}
// --- Stern zeichnen ---
function drawStar(cx, cy, r, points) {
ctx.beginPath();
for (let i = 0; i < points * 2; i++) {
const angle = (i * Math.PI) / points - Math.PI / 2;
const radius = i % 2 === 0 ? r : r * 0.4;
const px = cx + radius * Math.cos(angle);
const py = cy + radius * Math.sin(angle);
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.closePath();
}
// --- Fee zeichnen ---
function drawFairy(x, y) {
const t = performance.now() / 1000;
const fairyAngle = Math.sin(t * 3) * 0.18;
ctx.save();
ctx.translate(x, y);
ctx.rotate(fairyAngle);
// Flügel
const wingFlap = Math.sin(t * 12) * 0.3;
ctx.globalAlpha = 0.55;
// Linker oberer Flügel
ctx.save();
ctx.rotate(-0.3 - wingFlap * 0.4);
ctx.scale(1, 0.6);
ctx.beginPath();
ctx.ellipse(-10, -4, 13, 8, -0.5, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255,182,213,0.7)';
ctx.fill();
ctx.strokeStyle = 'rgba(255,100,180,0.5)';
ctx.lineWidth = 0.5;
ctx.stroke();
ctx.restore();
// Linker unterer Flügel
ctx.save();
ctx.rotate(-0.1 - wingFlap * 0.3);
ctx.scale(1, 0.5);
ctx.beginPath();
ctx.ellipse(-7, 4, 9, 5, 0.3, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255,182,213,0.6)';
ctx.fill();
ctx.restore();
// Rechter oberer Flügel
ctx.save();
ctx.rotate(0.3 + wingFlap * 0.4);
ctx.scale(1, 0.6);
ctx.beginPath();
ctx.ellipse(10, -4, 13, 8, 0.5, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255,182,213,0.7)';
ctx.fill();
ctx.strokeStyle = 'rgba(255,100,180,0.5)';
ctx.lineWidth = 0.5;
ctx.stroke();
ctx.restore();
// Rechter unterer Flügel
ctx.save();
ctx.rotate(0.1 + wingFlap * 0.3);
ctx.scale(1, 0.5);
ctx.beginPath();
ctx.ellipse(7, 4, 9, 5, -0.3, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255,182,213,0.6)';
ctx.fill();
ctx.restore();
ctx.globalAlpha = 1;
// Körper
ctx.beginPath();
ctx.ellipse(0, 0, 5, 8, 0, 0, Math.PI * 2);
ctx.fillStyle = '#FF69B4';
ctx.fill();
// Kopf
ctx.beginPath();
ctx.arc(0, -10, 5, 0, Math.PI * 2);
ctx.fillStyle = '#FFD1A4';
ctx.fill();
// Haare
ctx.beginPath();
ctx.arc(0, -13, 5, Math.PI, 0);
ctx.fillStyle = '#FFD700';
ctx.fill();
// Augen
ctx.fillStyle = '#4A2040';
ctx.beginPath();
ctx.arc(-2, -10, 1, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(2, -10, 1, 0, Math.PI * 2);
ctx.fill();
// Zauberstab
ctx.save();
ctx.rotate(0.4);
ctx.strokeStyle = '#C4A040';
ctx.lineWidth = 1.2;
ctx.beginPath();
ctx.moveTo(5, -2);
ctx.lineTo(16, -14);
ctx.stroke();
// Zauberstab-Stern (pulsierend)
const ws = 3 + Math.sin(t * 6) * 0.8;
drawStar(16, -14, ws, 5);
ctx.fillStyle = '#FFD700';
ctx.fill();
ctx.restore();
ctx.restore();
}
// --- Animationsloop ---
function loop() {
ctx.clearRect(0, 0, W, H);
if (mx > -100) {
const dx = mx - prevMx;
const dy = my - prevMy;
const speed = Math.sqrt(dx * dx + dy * dy);
const moving = speed > 1.5;
if (moving) {
spawnParticles(mx, my, true);
idleFrame = 0;
} else {
idleFrame++;
if (idleFrame >= IDLE_INTERVAL) {
spawnParticles(mx, my, false);
idleFrame = 0;
}
}
prevMx = mx;
prevMy = my;
}
// Partikel aktualisieren & zeichnen
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.vx;
p.y += p.vy;
p.vy += 0.02;
p.life -= p.decay;
p.twinkle += 0.2;
if (p.life <= 0) { particles.splice(i, 1); continue; }
ctx.save();
ctx.globalAlpha = p.life * (0.7 + 0.3 * Math.sin(p.twinkle));
ctx.fillStyle = p.color;
if (p.shape === 'star') {
drawStar(p.x, p.y, p.size, 4);
ctx.fill();
} else {
ctx.beginPath();
ctx.arc(p.x, p.y, p.size * 0.7, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
}
// Fee zeichnen
if (mx > -100) drawFairy(mx, my);
requestAnimationFrame(loop);
}
loop();
})();
</script>