console
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>光点落水波纹</title>
<style>
*{margin:0;padding:0}
body{background:#000;overflow:hidden}
canvas{display:block}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
function resize(){
canvas.width = innerWidth;
canvas.height = innerHeight;
}
resize();
window.addEventListener('resize',resize);
// 粒子:下落光点
class Particle{
constructor(){
this.reset();
}
reset(){
this.x = Math.random()*canvas.width;
this.y = -10;
this.vy = 3 + Math.random()*3;
this.r = 1 + Math.random()*2;
}
update(){
this.y += this.vy;
// 落到下方,生成波纹
if(this.y >= canvas.height - 20){
ripples.push(new Ripple(this.x, canvas.height - 20));
this.reset();
}
}
draw(){
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,Math.PI*2);
ctx.fillStyle='#fff';
ctx.fill();
}
}
// 波纹:圆环扩散淡出
class Ripple{
constructor(x,y){
this.x = x;
this.y = y;
this.radius = 0;
this.maxR = 40 + Math.random()*30;
this.alpha = 1;
this.speed = 1.2;
}
update(){
this.radius += this.speed;
this.alpha -= 0.01;
}
draw(){
if(this.alpha <=0) return;
ctx.save();
ctx.strokeStyle=`rgba(255,255,255,${this.alpha})`;
ctx.lineWidth=1;
ctx.beginPath();
ctx.arc(this.x,this.y,this.radius,0,Math.PI*2);
ctx.stroke();
ctx.restore();
}
}
const particles = [];
const ripples = [];
// 生成光点数量
for(let i=1;i<30;i++){
particles.push(new Particle());
}
function loop(){
// 拖影,不是完全清屏,柔和一点
ctx.fillStyle='rgba(0,0,0,0.08)';
ctx.fillRect(0,0,canvas.width,canvas.height);
particles.forEach(p=>{
p.update();
p.draw();
})
for(let i=ripples.length-1;i>=0;i--){
const r = ripples[i];
r.update();
r.draw();
if(r.alpha <= 0){
ripples.splice(i,1);
}
}
requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>