效果演示

html 代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Enhanced Canvas Animation</title>
<style>
html, body { margin: 0; padding: 0; width: 100%; height: 100%; background: radial-gradient(circle, #000000, #131862, #1c1c5c); background-size: 300% 300%; animation: gradientBG 15s ease infinite; }
canvas { display: block; }
@keyframes gradientBG { 0% { background-position: 0% 0%; } 50% { background-position: 100% 100%; } 100% { background-position: 0% 0%; } }
</style>
</head>
<body>
<canvas></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script>
const c = document.querySelector('canvas'); const ctx = c.getContext('2d'); let cw = c.width = innerWidth; let ch = c.height = innerHeight; const ticks = 200; const ring1 = []; const ring2 = []; const particles = []; const dur = 14;
for (let i = 0; i < ticks; i++) { const angle = i / ticks * Math.PI * 2; const radius = 270; ring1[i] = { x1: 0, x2: 0, y1: 0, y2: 0, lineWidth: 8, a: angle, r: radius, h: 180 + gsap.utils.wrapYoyo(0, 50, i / ticks * 180), glow: 15 }; ring2[i] = { x1: 0, x2: 0, y1: 0, y2: 0, lineWidth: 3, a: angle, r: radius / 2, h: 180 + gsap.utils.wrapYoyo(0, 60, i / ticks * 180), glow: 8 }; }
function createParticles() { for (let i = 0; i < 300; i++) { particles.push({ x: Math.random() * cw, y: Math.random() * ch, size: Math.random() * 2 + 1, alpha: Math.random() * 0.5 + 0.5, speed: Math.random() * 0.5 + 0.5 }); } }
createParticles();
const tl = gsap.timeline({ onUpdate: update }).fromTo([ring1, ring2], { x1: (i, t) => Math.cos(t.a) * t.r * -1.5, y1: (i, t) => Math.sin(t.a) * t.r * -1.5, x2: (i, t) => Math.cos(t.a) * t.r * 1.5, y2: (i, t) => Math.sin(t.a) * t.r * 1.5 }, { x1: (i, t) => Math.cos(t.a) * t.r * 0.3, y1: (i, t) => Math.sin(t.a) * t.r * 0.3, x2: (i, t) => Math.cos(t.a) * t.r * 0.15, y2: (i, t) => Math.sin(t.a) * t.r * 0.15, duration: dur / 2, ease: 'power4', repeat: -1, yoyo: true }, 0).to(ring1, { lineWidth: 1, h: '+=150', glow: '+=25', duration: dur * 0.25, ease: 'power4.inOut', yoyoEase: 'power2.in', stagger: { amount: dur, from: 0, repeat: -1, yoyo: true } }, 0).play(dur * 1.2);
function drawPath(t) { ctx.strokeStyle = 'hsl(' + t.h + ',100%,50%)'; ctx.lineCap = "round"; ctx.lineWidth = t.lineWidth; ctx.setLineDash([t.lineWidth * 2, 40]); ctx.shadowBlur = t.glow; ctx.shadowColor = ctx.strokeStyle; ctx.beginPath(); ctx.moveTo(t.x1 + cw / 2, t.y1 + ch / 2); ctx.lineTo(t.x2 + cw / 2, t.y2 + ch / 2); ctx.stroke(); }
function drawParticles() { particles.forEach(p => { ctx.fillStyle = 'rgba(255, 255, 255,' + p.alpha + ')'; ctx.beginPath(); ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); ctx.fill(); p.y -= p.speed; if (p.y < 0) p.y = ch; }); }
function update() { ctx.clearRect(0, 0, cw, ch); ring1.forEach(drawPath); ring2.forEach(drawPath); drawParticles(); }
window.onresize = () => { cw = c.width = innerWidth; ch = c.height = innerHeight; update(); };
window.onpointerup = () => { gsap.to(tl, { duration: 1, ease: 'power3', timeScale: (tl.isActive() ? 0 : 1) }); };
</script>
</body>
</html>