/** * 文件功能:聊天室下雨特效 * * 使用 Canvas 绘制斜向雨线,模拟真实降雨视觉效果。 * 特效总时长约 8 秒,结束后自动清理并回调。 */ const RainEffect = (() => { // 雨滴类:一条从顶部往下落的斜线 class Drop { constructor(w, h) { this.reset(w, h); } /** * 重置/初始化雨滴位置 * * @param {number} w Canvas 宽度 * @param {number} h Canvas 高度 */ reset(w, h) { this.x = Math.random() * w; this.y = Math.random() * -h; // 从屏幕上方随机位置开始 this.len = Math.random() * 20 + 10; // 雨线长度 this.speed = Math.random() * 8 + 6; // 下落速度 this.angle = (Math.PI / 180) * (75 + Math.random() * 10); // 倾斜角(接近竖直偏右) this.alpha = Math.random() * 0.3 + 0.2; // 透明度 this.w = w; this.h = h; } /** 每帧更新雨滴位置 */ update() { this.x += Math.cos(this.angle) * this.speed * 0.3; this.y += Math.sin(this.angle) * this.speed; // 落出屏幕后重置 if (this.y > this.h + this.len) { this.reset(this.w, this.h); } } /** 绘制雨滴线段 */ draw(ctx) { ctx.save(); ctx.strokeStyle = `rgba(155, 200, 255, ${this.alpha})`; ctx.lineWidth = 0.8; ctx.beginPath(); ctx.moveTo(this.x, this.y); ctx.lineTo( this.x + Math.cos(this.angle) * this.len, this.y + Math.sin(this.angle) * this.len, ); ctx.stroke(); ctx.restore(); } } /** * 启动下雨特效 * * @param {HTMLCanvasElement} canvas 全屏 Canvas * @param {Function} onEnd 特效结束回调 */ function start(canvas, onEnd) { const ctx = canvas.getContext("2d"); const w = canvas.width; const h = canvas.height; const DURATION = 8000; // 总时长(ms) const DROP_COUNT = 180; // 雨滴数量 // 初始化所有雨滴,随机分布在屏幕各处(避免开始时从顶部一起落) const drops = Array.from({ length: DROP_COUNT }, () => { const d = new Drop(w, h); d.y = Math.random() * h; // 初始 Y 随机,不全部从顶部开始 return d; }); let animId = null; const startTime = performance.now(); function animate(now) { // 清除画布(保持透明,不遮挡聊天背景) ctx.clearRect(0, 0, w, h); drops.forEach((d) => { d.update(); d.draw(ctx); }); if (now - startTime < DURATION) { animId = requestAnimationFrame(animate); } else { cancelAnimationFrame(animId); ctx.clearRect(0, 0, w, h); onEnd(); } } animId = requestAnimationFrame(animate); } return { start }; })();