119 lines
3.6 KiB
JavaScript
119 lines
3.6 KiB
JavaScript
/**
|
|
* 文件功能:聊天室樱花飘落特效
|
|
*
|
|
* 使用 Canvas 绘制多层粉色花瓣,让花瓣在屏幕上方生成后缓慢下落、
|
|
* 左右摆动并带轻微旋转,营造柔和的春日氛围。
|
|
*/
|
|
|
|
const SakuraEffect = (() => {
|
|
class Petal {
|
|
constructor(w, h) {
|
|
this.w = w;
|
|
this.h = h;
|
|
this.reset(true);
|
|
}
|
|
|
|
/**
|
|
* 重置花瓣位置与运动参数。
|
|
*
|
|
* @param {boolean} initial 是否首次初始化
|
|
*/
|
|
reset(initial = false) {
|
|
this.x = Math.random() * this.w;
|
|
this.y = initial ? Math.random() * this.h : -20;
|
|
this.size = Math.random() * 7 + 8;
|
|
this.speedY = Math.random() * 1.1 + 0.7;
|
|
this.speedX = Math.random() * 0.6 - 0.3;
|
|
this.swing = Math.random() * Math.PI * 2;
|
|
this.swingSpeed = Math.random() * 0.03 + 0.01;
|
|
this.rotation = Math.random() * Math.PI * 2;
|
|
this.rotationSpeed = (Math.random() - 0.5) * 0.03;
|
|
this.alpha = Math.random() * 0.25 + 0.55;
|
|
this.color = ["#fbcfe8", "#f9a8d4", "#fda4af", "#fce7f3"][
|
|
Math.floor(Math.random() * 4)
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 更新花瓣运动状态。
|
|
*/
|
|
update() {
|
|
this.swing += this.swingSpeed;
|
|
this.rotation += this.rotationSpeed;
|
|
this.x += this.speedX + Math.sin(this.swing) * 0.75;
|
|
this.y += this.speedY;
|
|
|
|
if (this.y > this.h + 30 || this.x < -30 || this.x > this.w + 30) {
|
|
this.reset(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 绘制单片樱花花瓣。
|
|
*
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
*/
|
|
draw(ctx) {
|
|
ctx.save();
|
|
ctx.translate(this.x, this.y);
|
|
ctx.rotate(this.rotation);
|
|
ctx.globalAlpha = this.alpha;
|
|
ctx.fillStyle = this.color;
|
|
ctx.shadowColor = "rgba(244, 114, 182, 0.35)";
|
|
ctx.shadowBlur = 8;
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(0, -this.size * 0.9);
|
|
ctx.quadraticCurveTo(this.size * 0.9, -this.size * 0.25, 0, this.size);
|
|
ctx.quadraticCurveTo(-this.size * 0.9, -this.size * 0.25, 0, -this.size * 0.9);
|
|
ctx.fill();
|
|
|
|
ctx.strokeStyle = "rgba(190, 24, 93, 0.25)";
|
|
ctx.lineWidth = 1;
|
|
ctx.beginPath();
|
|
ctx.moveTo(0, -this.size * 0.55);
|
|
ctx.lineTo(0, this.size * 0.7);
|
|
ctx.stroke();
|
|
ctx.restore();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 启动樱花特效。
|
|
*
|
|
* @param {HTMLCanvasElement} canvas
|
|
* @param {Function} onEnd
|
|
*/
|
|
function start(canvas, onEnd) {
|
|
const ctx = canvas.getContext("2d");
|
|
const w = canvas.width;
|
|
const h = canvas.height;
|
|
const DURATION = 10000;
|
|
const PETAL_COUNT = Math.min(58, Math.max(34, Math.floor(w / 28)));
|
|
const petals = Array.from({ length: PETAL_COUNT }, () => new Petal(w, h));
|
|
const startTime = performance.now();
|
|
let animId = null;
|
|
|
|
function animate(now) {
|
|
ctx.clearRect(0, 0, w, h);
|
|
|
|
petals.forEach((petal) => {
|
|
petal.update();
|
|
petal.draw(ctx);
|
|
});
|
|
|
|
if (now - startTime < DURATION) {
|
|
animId = requestAnimationFrame(animate);
|
|
} else {
|
|
cancelAnimationFrame(animId);
|
|
ctx.clearRect(0, 0, w, h);
|
|
onEnd();
|
|
}
|
|
}
|
|
|
|
animId = requestAnimationFrame(animate);
|
|
}
|
|
|
|
return { start };
|
|
})();
|