2025-04-04 11:01:29 +08:00

50 lines
2.0 KiB
JavaScript

export default (element, options) => {
document.querySelectorAll('.vh-paopao').forEach(item => setTimeout(() => item.remove()));
const config = Object.assign({ radius: 10, density: 0.3, clearOffset: 0.2 }, options);
let width, height, ctx, active = true;
const canvas = document.createElement('canvas');
const particles = [];
// 初始化画布
const initCanvas = () => {
width = element.offsetWidth;
height = element.offsetHeight;
Object.assign(canvas.style, { top: '0', zIndex: '0', position: 'absolute', 'pointer-events': 'none' });
element.append(canvas);
element.parentElement.style.overflow = 'hidden';
canvas.width = width;
canvas.height = height;
canvas.classList.add('vh-paopao');
ctx = canvas.getContext('2d');
};
// 粒子类
class Particle {
constructor() { this.reset() }
reset() {
this.x = Math.random() * width;
this.y = height + 100 * Math.random();
this.alpha = 0.1 + Math.random() * config.clearOffset;
this.scale = 0.1 + 0.3 * Math.random();
this.speed = Math.random();
this.color = config.color === "random" ? `rgba(${Math.random() * 255 | 0},0,0,${Math.random().toFixed(2)})` : config.color;
}
draw() {
if (this.alpha <= 0) this.reset();
this.y -= this.speed;
this.alpha -= 0.0005;
ctx.beginPath();
ctx.arc(this.x, this.y, this.scale * config.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
}
}
// 初始化
initCanvas();
// 动画循环
const animate = () => { active && ctx.clearRect(0, 0, width, height); particles.forEach(p => p.draw()); requestAnimationFrame(animate); };
Array.from({ length: width * config.density | 0 }, () => particles.push(new Particle()));
animate();
// 事件监听
window.addEventListener('scroll', () => active = document.documentElement.scrollTop <= height);
window.addEventListener('resize', () => { width = element.clientWidth; height = element.clientHeight; canvas.width = width; canvas.height = height; });
};