From 998611f4ba936310038c6bf83a5d9283e62052b6 Mon Sep 17 00:00:00 2001 From: lkddi Date: Fri, 27 Feb 2026 14:29:03 +0800 Subject: [PATCH] =?UTF-8?q?Fix:=20=E9=9B=AA=E8=8A=B1=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=B7=B1=E8=93=9D=E8=BD=AE=E5=BB=93+=E7=99=BD=E8=89=B2?= =?UTF-8?q?=E4=B8=BB=E4=BD=93=E5=8F=8C=E9=81=8D=E7=BB=98=E5=88=B6=EF=BC=8C?= =?UTF-8?q?=E6=B5=85=E8=89=B2=E8=83=8C=E6=99=AF=E4=B8=8B=E6=B8=85=E6=99=B0?= =?UTF-8?q?=E5=8F=AF=E8=BE=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/effects/snow.js | 106 +++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/public/js/effects/snow.js b/public/js/effects/snow.js index a7dd9cf..0ba5b4f 100644 --- a/public/js/effects/snow.js +++ b/public/js/effects/snow.js @@ -2,70 +2,76 @@ * 文件功能:聊天室下雪特效 * * 使用 Canvas 绘制真实六角雪花图案(6条主臂 + 左右分叉)。 - * 雪花大小、速度、旋转角度随机,自然飘落效果。 + * 采用深色描边+白色主体双遍绘制,在任何背景颜色上都清晰可见。 * 特效总时长约 10 秒,结束后自动清理并回调。 */ const SnowEffect = (() => { /** - * 在指定位置绘制一朵六角雪花 + * 在指定位置绘制一朵六角雪花(深色轮廓 + 白色主体) * * @param {CanvasRenderingContext2D} ctx * @param {number} x 中心 x * @param {number} y 中心 y - * @param {number} r 主臂长度(半径) + * @param {number} r 主臂长度 * @param {number} alpha 透明度 * @param {number} rot 旋转角度(弧度) */ function _drawFlake(ctx, x, y, r, alpha, rot) { ctx.save(); ctx.globalAlpha = alpha; - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = Math.max(1, r * 0.12); - ctx.shadowColor = "rgba(180, 210, 255, 0.9)"; - ctx.shadowBlur = 4; ctx.lineCap = "round"; - ctx.translate(x, y); ctx.rotate(rot); - // 绘制 6 条主臂(每 60° 一条) - for (let i = 0; i < 6; i++) { - ctx.save(); - ctx.rotate((Math.PI / 3) * i); + // 两遍绘制:先深蓝色粗描边,再白色细线覆盖 + // 这样在浅蓝、白色等背景上都清晰可辨 + const passes = [ + { color: "rgba(30, 60, 140, 0.8)", lw: r * 0.22 + 2.5 }, // 深蓝粗描边 + { color: "rgba(255, 255, 255, 1.0)", lw: Math.max(1, r * 0.11) }, // 白色主体 + ]; - // 主臂 - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.lineTo(r, 0); - ctx.stroke(); + passes.forEach(({ color, lw }) => { + ctx.strokeStyle = color; + ctx.lineWidth = lw; - // 主臂上的两段斜向分叉(在 0.4r 和 0.65r 处) - const branchLen = r * 0.35; - const branchAngle = Math.PI / 4; // 45° + for (let i = 0; i < 6; i++) { + ctx.save(); + ctx.rotate((Math.PI / 3) * i); - [0.4, 0.65].forEach((pos) => { - const bx = r * pos; - // 上分叉 + // 主臂 ctx.beginPath(); - ctx.moveTo(bx, 0); - ctx.lineTo( - bx + Math.cos(branchAngle) * branchLen, - Math.sin(branchAngle) * branchLen, - ); + ctx.moveTo(0, 0); + ctx.lineTo(r, 0); ctx.stroke(); - // 下分叉(对称) - ctx.beginPath(); - ctx.moveTo(bx, 0); - ctx.lineTo( - bx + Math.cos(branchAngle) * branchLen, - -Math.sin(branchAngle) * branchLen, - ); - ctx.stroke(); - }); - ctx.restore(); - } + // 斜向分叉(0.4r 和 0.65r 处各一对) + const branchLen = r * 0.35; + const branchAngle = Math.PI / 4; // 45° + + [0.4, 0.65].forEach((pos) => { + const bx = r * pos; + // 上分叉 + ctx.beginPath(); + ctx.moveTo(bx, 0); + ctx.lineTo( + bx + Math.cos(branchAngle) * branchLen, + Math.sin(branchAngle) * branchLen, + ); + ctx.stroke(); + // 下分叉 + ctx.beginPath(); + ctx.moveTo(bx, 0); + ctx.lineTo( + bx + Math.cos(branchAngle) * branchLen, + -Math.sin(branchAngle) * branchLen, + ); + ctx.stroke(); + }); + + ctx.restore(); + } + }); ctx.restore(); } @@ -78,25 +84,19 @@ const SnowEffect = (() => { this.reset(true); } - /** - * 重置雪花位置 - * - * @param {boolean} initial 初始化时 Y 随机分布全屏 - */ reset(initial = false) { this.x = Math.random() * this.w; this.y = initial ? Math.random() * this.h : -20; - this.r = Math.random() * 10 + 6; // 主臂长度 6-16px - this.speed = Math.random() * 1.2 + 0.4; // 下落速度(慢慢飘) - this.drift = Math.random() * 0.6 - 0.3; // 水平漂移 + this.r = Math.random() * 10 + 7; // 主臂长度 7-17px(略放大) + this.speed = Math.random() * 1.2 + 0.4; + this.drift = Math.random() * 0.6 - 0.3; this.alpha = Math.random() * 0.3 + 0.7; // 透明度 0.7-1.0 - this.rot = Math.random() * Math.PI * 2; // 初始旋转角 - this.rotSpd = (Math.random() - 0.5) * 0.02; // 旋转速度 + this.rot = Math.random() * Math.PI * 2; + this.rotSpd = (Math.random() - 0.5) * 0.02; this.wobble = 0; - this.wobSpd = Math.random() * 0.03 + 0.01; // 摇摆频率 + this.wobSpd = Math.random() * 0.03 + 0.01; } - /** 每帧更新 */ update() { this.wobble += this.wobSpd; this.x += Math.sin(this.wobble) * 0.5 + this.drift; @@ -107,7 +107,6 @@ const SnowEffect = (() => { } } - /** 绘制雪花 */ draw(ctx) { _drawFlake(ctx, this.x, this.y, this.r, this.alpha, this.rot); } @@ -124,9 +123,8 @@ const SnowEffect = (() => { const w = canvas.width; const h = canvas.height; const DURATION = 10000; - const FLAKE_COUNT = 80; // 六角雪花绘制开销较大,80 个足够 + const FLAKE_COUNT = 80; - // 初始化所有雪花,随机分布全屏 const flakes = Array.from( { length: FLAKE_COUNT }, () => new Flake(w, h),