Add new chat effects and shop items
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* 文件功能:聊天室爱心飘落特效
|
||||
*
|
||||
* 在透明 Canvas 上绘制成组心形,从上方向下飘落并轻微摇摆,
|
||||
* 适用于表白、婚礼、节日祝福等氛围场景。
|
||||
*/
|
||||
|
||||
const HeartsEffect = (() => {
|
||||
class Heart {
|
||||
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 : -24;
|
||||
this.size = Math.random() * 10 + 10;
|
||||
this.speedY = Math.random() * 1.5 + 1.1;
|
||||
this.swing = Math.random() * Math.PI * 2;
|
||||
this.swingSpeed = Math.random() * 0.04 + 0.015;
|
||||
this.rotation = (Math.random() - 0.5) * 0.35;
|
||||
this.rotationSpeed = (Math.random() - 0.5) * 0.01;
|
||||
this.alpha = Math.random() * 0.28 + 0.62;
|
||||
this.color = ["#fb7185", "#f43f5e", "#ec4899", "#fda4af"][
|
||||
Math.floor(Math.random() * 4)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新爱心位置。
|
||||
*/
|
||||
update() {
|
||||
this.swing += this.swingSpeed;
|
||||
this.rotation += this.rotationSpeed;
|
||||
this.x += Math.sin(this.swing) * 1.1;
|
||||
this.y += this.speedY;
|
||||
|
||||
if (this.y > this.h + 28) {
|
||||
this.reset(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制爱心形状。
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
*/
|
||||
draw(ctx) {
|
||||
const s = this.size;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(this.x, this.y);
|
||||
ctx.rotate(this.rotation);
|
||||
ctx.scale(s / 16, s / 16);
|
||||
ctx.globalAlpha = this.alpha;
|
||||
ctx.fillStyle = this.color;
|
||||
ctx.shadowColor = "rgba(244, 63, 94, 0.45)";
|
||||
ctx.shadowBlur = 10;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, -4);
|
||||
ctx.bezierCurveTo(12, -18, 26, -2, 0, 18);
|
||||
ctx.bezierCurveTo(-26, -2, -12, -18, 0, -4);
|
||||
ctx.fill();
|
||||
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 = 9000;
|
||||
const HEART_COUNT = Math.min(48, Math.max(24, Math.floor(w / 34)));
|
||||
const hearts = Array.from({ length: HEART_COUNT }, () => new Heart(w, h));
|
||||
const startTime = performance.now();
|
||||
let animId = null;
|
||||
|
||||
function animate(now) {
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
|
||||
hearts.forEach((heart) => {
|
||||
heart.update();
|
||||
heart.draw(ctx);
|
||||
});
|
||||
|
||||
if (now - startTime < DURATION) {
|
||||
animId = requestAnimationFrame(animate);
|
||||
} else {
|
||||
cancelAnimationFrame(animId);
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
onEnd();
|
||||
}
|
||||
}
|
||||
|
||||
animId = requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
return { start };
|
||||
})();
|
||||
Reference in New Issue
Block a user