如何为HTML5 Canvas瀑布粒子效果添加蓝色模糊渐变效果?
如何为HTML5 Canvas瀑布粒子效果添加蓝色模糊渐变效果?
嘿,我看了你用HTML5 Canvas做的瀑布粒子效果,想要实现参考图里那种粒子逐渐淡出并变成蓝色模糊的效果对吧?你提到的快速模糊思路完全可行,我来给你具体讲讲怎么修改现有代码来达成这个效果~
核心思路:颜色渐变 + 动态模糊叠加
参考图里的效果是粒子从接近白色的亮色,随着淡出逐渐过渡为模糊的蓝色。我们可以通过两个关键调整来实现:
- 让粒子的颜色随透明度(alpha)动态变化,从浅亮色调过渡到深蓝色
- 给淡出阶段的粒子添加动态模糊,透明度越低,模糊程度越高
具体代码修改步骤
1. 调整粒子颜色渐变逻辑
原来的粒子颜色是固定的蓝色调,我们可以修改fillStyle,让亮度随alpha值变化:当粒子刚生成(alpha=1)时亮度接近白色,随着alpha降低,亮度逐渐回到深蓝。
修改Particle类的draw()方法:
draw() { // 根据粒子类型和alpha动态调整模糊程度 const blurAmount = this.isSplinter ? (3 - (this.alpha * 3)) : (2 - (this.alpha * 2)); ctx.filter = `blur(${blurAmount}px)`; // 动态调整亮度,实现从亮到蓝的渐变 const brightness = 67 + (30 * this.alpha); ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); ctx.fillStyle = `hsla(199, 100%, ${brightness}%, ${this.alpha})`; // 随alpha变化的颜色 ctx.fill(); // 重置滤镜,避免影响后续绘制 ctx.filter = 'none'; }
2. 优化画布的拖尾效果
原来的画布清空用的是rgba(0,0,0,0.2),可以稍微调整透明度,让模糊效果的残留更自然,比如改成rgba(0,0,0,0.15),这样拖尾会更柔和,和模糊效果更搭配:
在animate()函数里修改:
ctx.fillStyle = 'rgba(0, 0, 0, 0.15)'; // 更柔和的拖尾,适配模糊效果 ctx.fillRect(0, 0, canvas.width, canvas.height);
修改后的完整代码
<canvas id="fountainCanvas"></canvas>
canvas { display: block; margin: 0 auto; background: #000; }
const canvas = document.getElementById('fountainCanvas'); const ctx = canvas.getContext('2d'); // Set canvas size canvas.width = window.innerWidth; canvas.height = window.innerHeight; const particles = []; const splinters = []; const gravity = 0.1; // Gravity constant const fountainX = canvas.width / 2; const fountainY = canvas.height / 2; // Mouse position let mouseX = fountainX; let mouseY = fountainY; // Particle class class Particle { constructor(x, y, angle, isSplinter = false) { this.x = x; this.y = y; const speed = Math.random() * 3 + 2; // Random speed const spread = Math.random() * 0.4 - 0.2; // Randomize direction slightly this.vx = isSplinter ? (Math.random() * 2 - 1) * 3 : Math.cos(angle + spread) * speed; this.vy = isSplinter ? Math.random() * -3 : Math.sin(angle + spread) * speed; this.alpha = isSplinter ? 1 : 1; // Opacity this.radius = isSplinter ? Math.random() : Math.random() + 1; // Size this.isSplinter = isSplinter; } update() { this.x += this.vx; this.y += this.vy; this.vy += gravity; // Apply gravity this.alpha -= this.isSplinter ? 0.02 : 0.005; // Fade out // Check if main particles reach the bottom of the canvas if (!this.isSplinter && this.y >= canvas.height) { this.createSplinters(); // Create splinters on impact this.alpha = 0; // Make particle invisible } } createSplinters() { for (let i = 0; i < 10; i++) { splinters.push(new Particle(this.x, canvas.height, 0, true)); } } draw() { // 根据粒子类型和alpha动态调整模糊程度 const blurAmount = this.isSplinter ? (3 - (this.alpha * 3)) : (2 - (this.alpha * 2)); ctx.filter = `blur(${blurAmount}px)`; // 动态调整亮度,实现从亮到蓝的渐变 const brightness = 67 + (30 * this.alpha); ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); ctx.fillStyle = `hsla(199, 100%, ${brightness}%, ${this.alpha})`; // 随alpha变化的颜色 ctx.fill(); // 重置滤镜,避免影响其他绘制 ctx.filter = 'none'; } isAlive() { return this.alpha > 0; // Check if particle is still visible } } // Create particles over time function emitParticles() { const angle = Math.atan2(mouseY - fountainY, mouseX - fountainX); for (let i = 0; i < 5; i++) { // Emit a few particles per frame particles.push(new Particle(fountainX, fountainY, angle)); } } // Animation loop function animate() { ctx.fillStyle = 'rgba(0, 0, 0, 0.15)'; // 更柔和的拖尾,适配模糊效果 ctx.fillRect(0, 0, canvas.width, canvas.height); emitParticles(); // Emit new particles continuously // Update and draw particles particles.forEach((particle, index) => { particle.update(); if (!particle.isAlive()) { particles.splice(index, 1); // Remove dead particles } else { particle.draw(); } }); // Update and draw splinters splinters.forEach((splinter, index) => { splinter.update(); if (!splinter.isAlive()) { splinters.splice(index, 1); // Remove dead splinters } else { splinter.draw(); } }); requestAnimationFrame(animate); } // Update mouse position on move canvas.addEventListener('mousemove', (event) => { mouseX = event.clientX; mouseY = event.clientY; }); // Initialize animation animate();
关于Simplex Noise的补充思路
如果想要更自然的流体扰动效果(比如粒子运动时的轻微随机偏移),可以把Simplex Noise的核心函数直接写进代码里,在update()方法里用噪声值调整粒子的vx/vy,让粒子运动更接近真实水流的不规则感。不过对于你想要的淡出模糊效果,上面的颜色+动态模糊已经足够啦~
备注:内容来源于stack exchange,提问作者satyanarayan mishra




