76f266645d
含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。 排除 node_modules、构建产物、安装包与 .env 密钥。 Co-authored-by: Cursor <cursoragent@cursor.com>
68 lines
2.0 KiB
TypeScript
68 lines
2.0 KiB
TypeScript
import { useEffect, useRef } from 'react';
|
|
|
|
export default function AmbientBackground() {
|
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
|
|
|
useEffect(() => {
|
|
const canvas = canvasRef.current;
|
|
if (!canvas) return;
|
|
const ctx = canvas.getContext('2d');
|
|
if (!ctx) return;
|
|
|
|
let frame = 0;
|
|
let width = (canvas.width = window.innerWidth);
|
|
let height = (canvas.height = window.innerHeight);
|
|
const onResize = () => {
|
|
width = canvas.width = window.innerWidth;
|
|
height = canvas.height = window.innerHeight;
|
|
};
|
|
window.addEventListener('resize', onResize);
|
|
|
|
const particles = Array.from({ length: 32 }, () => ({
|
|
x: Math.random() * width,
|
|
y: Math.random() * height,
|
|
size: Math.random() * 3 + 1.2,
|
|
speedY: -(Math.random() * 0.4 + 0.15),
|
|
speedX: (Math.random() - 0.5) * 0.2,
|
|
opacity: Math.random() * 0.5 + 0.2,
|
|
pulse: Math.random() * Math.PI,
|
|
pulseSpeed: Math.random() * 0.03 + 0.01,
|
|
}));
|
|
|
|
const render = () => {
|
|
ctx.clearRect(0, 0, width, height);
|
|
for (const p of particles) {
|
|
p.y += p.speedY;
|
|
p.x += p.speedX;
|
|
p.pulse += p.pulseSpeed;
|
|
if (p.y < -10) {
|
|
p.y = height + 10;
|
|
p.x = Math.random() * width;
|
|
}
|
|
if (p.x < -10) p.x = width + 10;
|
|
if (p.x > width + 10) p.x = -10;
|
|
const a = p.opacity * (0.7 + 0.3 * Math.sin(p.pulse));
|
|
ctx.beginPath();
|
|
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
|
|
ctx.fillStyle = `rgba(107, 56, 212, ${a * 0.45})`;
|
|
ctx.fill();
|
|
}
|
|
frame = requestAnimationFrame(render);
|
|
};
|
|
render();
|
|
return () => {
|
|
window.removeEventListener('resize', onResize);
|
|
cancelAnimationFrame(frame);
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<div className="yzl-ambient" aria-hidden>
|
|
<div className="yzl-orb yzl-orb-a" />
|
|
<div className="yzl-orb yzl-orb-b" />
|
|
<div className="yzl-orb yzl-orb-c" />
|
|
<canvas ref={canvasRef} className="yzl-particles" />
|
|
</div>
|
|
);
|
|
}
|