admin管理员组

文章数量:1336752

I need help to apply the water effect which is fading like the below image. In the image the white particles are fading to blue blurry effect. This is what I am trying to achieve.

I believe with fast blur or simplex-noise we should be able to achieve this, but not sure how to proceed.

(click to enlarge)

Achieved the water fall with splinters in HTML5 canvas.

CodePen Source

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() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
    ctx.fillStyle = `hsla(199, 100%, 67%, ${this.alpha})`; // Blue shade with opacity
    ctx.fill();
  }

  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.2)'; // Trail effect
  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();
canvas {
  display: block;
  margin: 0 auto;
  background: #000;
  /* Black background */
}
<canvas id="fountainCanvas"></canvas>

I need help to apply the water effect which is fading like the below image. In the image the white particles are fading to blue blurry effect. This is what I am trying to achieve.

I believe with fast blur or simplex-noise we should be able to achieve this, but not sure how to proceed.

(click to enlarge)

Achieved the water fall with splinters in HTML5 canvas.

CodePen Source

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() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
    ctx.fillStyle = `hsla(199, 100%, 67%, ${this.alpha})`; // Blue shade with opacity
    ctx.fill();
  }

  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.2)'; // Trail effect
  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();
canvas {
  display: block;
  margin: 0 auto;
  background: #000;
  /* Black background */
}
<canvas id="fountainCanvas"></canvas>

Share edited Nov 27, 2024 at 14:33 DarkBee 15.5k8 gold badges72 silver badges117 bronze badges asked Nov 19, 2024 at 18:23 satyanarayan mishrasatyanarayan mishra 1096 bronze badges 4
  • your code looks OK, what is the problem? – Helder Sepulveda Commented Nov 26, 2024 at 17:32
  • Looking to create the white to blue fade with blur – satyanarayan mishra Commented Nov 27, 2024 at 10:23
  • Applying blur to each particle wouldn't be sustainable. You may want to adapt the POC in that answer of mine: The idea is that you draw by layers based on the alpha. Here you'd add your desired amount of blur and switch the alpha to a color transition (or do it all in the filter) – Kaiido Commented Nov 28, 2024 at 0:30
  • Note that to switch from one color to another, you can use the convenient color-mix() css function, even in the canvas 2D API. – Kaiido Commented Nov 28, 2024 at 1:34
Add a comment  | 

3 Answers 3

Reset to default 1

Working on the effect

https://codepen.io/satya4satyanm/pen/YPKwBQV?editors=1010

Need to fix the gravity in this.

<div style="top: 50px; text-align:center">

    <canvas id="canvasOne" width="640" height="640">
        Your browser does not support HTML5 canvas.
    </canvas>

    <script src="https://dangries/rectangleworld/demos/Modernizr/modernizr-2.0.6.js"></script>
    <script src="https://dangries/rectangleworld/demos/Nova_Canvas/FastBlur.js"></script>

    <script>
        /*
Particle effect by Dan Gries, rectangleworld.
The FastBlur is created by Mario Klingemann, quasimondo. Sincere thanks to Mario for publicly sharing the code!
*/
        //for debug messages
        window.addEventListener("load", windowLoadHandler, false);
        var Debugger = function() {};
        Debugger.log = function(message) {
            try {
                console.log(message);
            } catch (exception) {
                return;
            }
        }

        function windowLoadHandler() {
            canvasApp();
        }

        function canvasSupport() {
            return Modernizr.canvas;
        }

        function canvasApp() {
            if (!canvasSupport()) {
                return;
            }
            var theCanvas = document.getElementById("canvasOne");
            var context = theCanvas.getContext("2d");
            var timer;
            var wait;
            var count;
            var particleList;
            var recycleBin;
            var emitX;
            var emitY;
            var displayWidth;
            var displayHeight;
            var particleAlpha;
            var baseColorR;
            var baseColorG;
            var baseColorB;
            var r;
            var g;
            var b;
            var phaseR;
            var phaseG;
            var phaseB;
            var targetPhaseR;
            var targetPhaseG;
            var targetPhaseB;
            var lastTargetPhaseR;
            var lastTargetPhaseG;
            var lastTargetPhaseB;
            var phaseShiftDuration;
            var phaseShiftCount;
            var particleColor;
            var numToAddEachFrame;
            var gravity = 3;
            init();

            function init() {
                bgColor = "#000000";
                context.fillStyle = bgColor;
                context.fillRect(0, 0, theCanvas.width, theCanvas.height);
                wait = 1;
                count = wait - 1;
                numToAddEachFrame = 2;
                particleAlpha = 1;
                targetPhaseR = 4;
                targetPhaseG = 3;
                targetPhaseB = 0;
                phaseShiftDuration = 2000;
                phaseShiftCount = phaseShiftDuration - 1;
                displayWidth = theCanvas.width;
                displayHeight = theCanvas.height;
                emitX = 100;//displayWidth / 2;
                emitY = 100;//displayHeight / 2;
                particleList = {};
                recycleBin = {};
                timer = setInterval(onTimer, 1000 / 24);
            }

            function onTimer() {
                count++;
                if (count >= wait) {
                    var time = Date.now();
                    r = 50;
                    g = 135;
                    b = 168;
                    count = 0;
                    for (i = 0; i < numToAddEachFrame; i++) {
                        var mag = 1.5 + 0.5 * (Math.random());
                        var angle = Math.random() / 3 * (Math.PI);
                        var p = addParticle(emitX, emitY, mag * Math.cos(angle), mag * Math.sin(angle));
                        p.color = "rgba(" + r + "," + g + "," + b + "," + particleAlpha + ")";
                        p.setEnvelope(10, 100, 50,
                            4, 10, 10,
                            0, 0.4 + 0.8 * Math.random(), 0);
                    }
                }
                updateParticles();
                drawScreen();
            }
            Particle.prototype.setEnvelope = function(a, h, d, av, hv, dv, r0, r1, r2) {
                this.attack = a + (2 * Math.random() - 1) * av;
                this.hold = h + (2 * Math.random() - 1) * hv;
                this.decay = d + (2 * Math.random() - 1) * dv;
                this.rInit = r0;
                this.rHold = r1;
                this.rLast = r2;
                this.rad = this.rInit;
            }

            function updateParticles() {
                var p = particleList.first;
                var outsideTest;
                var nextParticle;
                while (p != null) {
                    //before list is altered record next particle
                    nextParticle = p.next;
                    //add air resistance later
                    p.x += p.velX;
                    p.pvyg = p.velY * gravity;
                    p.y += p.pvyg;
                    //update age
                    p.age++;
                    //update size
                    if (p.age < p.attack + p.hold + p.decay) {
                        if (p.age < p.attack) {
                            p.rad = (p.rHold - p.rInit) / 15 / p.attack * p.age + p.rInit;
                        } else if (p.age < p.attack + p.hold) {
                            p.rad = p.rHold;
                        } else if (p.age < p.attack + p.hold + p.decay) {
                            p.rad = (p.rLast - p.rHold) / 15 / p.decay * (p.age - p.attack - p.hold) + p.rHold;
                        }
                    } else {
                        p.dead = true;
                    }
                    //test if particle is outside of window
                    outsideTest = (p.x + p.rad < 0) || (p.y + p.rad < 0) || (p.x > displayWidth - p.rad) || (p.y > displayWidth - p.rad);
                    if (outsideTest || p.dead) {
                        recycle(p);
                    }
                    p = nextParticle;
                }
            }

            function Particle() {
                //The particle class does not need anything in the constructor. Properties will be added dynamically.
                //This class is being used ony for anizational purposes - the "setEnvelope" function is attached to the prototype object
                //later.
            }

            function addParticle(x0, y0, vx0, vy0) {
                var newParticle;
                var color;
                //check recycle bin for available drop:
                if (recycleBin.first != null) {
                    newParticle = recycleBin.first;
                    //remove from bin
                    if (newParticle.next != null) {
                        recycleBin.first = newParticle.next;
                        newParticle.next.prev = null;
                    } else {
                        recycleBin.first = null;
                    }
                }
                //if the recycle bin is empty, create a new particle:
                else {
                    newParticle = new Particle();
                }
                //add to beginning of particle list
                if (particleList.first == null) {
                    particleList.first = newParticle;
                    newParticle.prev = null;
                    newParticle.next = null;
                } else {
                    newParticle.next = particleList.first;
                    particleList.first.prev = newParticle;
                    particleList.first = newParticle;
                    newParticle.prev = null;
                }
                //initialize
                newParticle.x = x0;
                newParticle.y = y0;
                newParticle.velX = vx0;
                newParticle.velY = vy0;
                newParticle.color = color;
                newParticle.age = 0;
                newParticle.dead = false;
                return newParticle;
            }

            function recycle(p) {
                //remove from particleList
                if (particleList.first == p) {
                    if (p.next != null) {
                        p.next.prev = null;
                        particleList.first = p.next;
                    } else {
                        particleList.first = null;
                    }
                } else {
                    if (p.next == null) {
                        p.prev.next = null;
                    } else {
                        p.prev.next = p.next;
                        p.next.prev = p.prev;
                    }
                }
                //add to recycle bin
                if (recycleBin.first == null) {
                    recycleBin.first = p;
                    p.prev = null; //may be unnecessary
                    p.next = null;
                } else {
                    p.next = recycleBin.first;
                    recycleBin.first.prev = p; //may be unnecessary
                    recycleBin.first = p;
                    p.prev = null; //may be unnecessary
                }
                //reset accel
            }

            function drawScreen() {
                //stack blur by Mario Klingemann, quasimondo
                boxBlurCanvasRGB("canvasOne", 0, 0, theCanvas.width, theCanvas.height, 2);
                var p = particleList.first;
                var pcount = 0;
                while (p != null) {
                    pcount++;
                    context.fillStyle = p.color;
                    context.beginPath();
                    context.arc(p.x, p.y, p.rad, 0, 2 * Math.PI, false);
                    context.closePath();
                    context.fill();
                    p = p.next;
                }
            }
        }
    </script>

In your code you fade the particles with the alpha:

ctx.fillStyle = `hsla(199, 100%, 67%, ${this.alpha})`;

But your background is black so the fade is from blue to black

If you want to create the illusion of fading from one color to another, you can draw two arc on the exact same location but the first one do it with a different color while that will be on the background, and should create the experience you are looking for...

const canvas = document.getElementById('fountainCanvas');
const ctx = canvas.getContext('2d');
var particles = []

class Particle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.alpha = 1;
  }

  draw() {
    this.alpha -= 0.005;
    ctx.beginPath();
    ctx.arc(this.x, this.y, 15, 0, Math.PI * 2);
    ctx.fillStyle = `red`;
    ctx.fill();
    ctx.beginPath();
    ctx.arc(this.x, this.y, 15, 0, Math.PI * 2);
    ctx.fillStyle = `hsla(199, 100%, 67%, ${this.alpha})`;
    ctx.fill();
  }
}

function emitParticles() {
  particles= []
  for (let i = 2; i < 7; i++) { 
    particles.push(new Particle(35*i, 20+10*i));
  }
}

function animate() {
  ctx.reset();
  particles.forEach(p => p.draw());
  requestAnimationFrame(animate);
}

setInterval(emitParticles, 4000)
emitParticles()
animate();
canvas {
  display: block;
  margin: 0 auto;
  background: #000;
  /* Black background */
}
<canvas id="fountainCanvas"></canvas>


You can also transition to other colors:
https://www.w3schools/colors/colors_hsl.asp

const canvas = document.getElementById('fountainCanvas');
const ctx = canvas.getContext('2d');
var particles = []

class Particle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.alpha = 1;
    this.color = 199
  }

  draw() {
    this.alpha -= 0.005;
    this.color -= 1;
    ctx.beginPath();
    ctx.arc(this.x, this.y, 15, 0, Math.PI * 2);
    ctx.fillStyle = `hsla(${this.color}, 100%, 67%, ${this.alpha})`;
    ctx.fill();
  }
}

function emitParticles() {
  particles= []
  for (let i = 2; i < 7; i++) { 
    particles.push(new Particle(35*i, 20+10*i));
  }
}

function animate() {
  ctx.reset();
  particles.forEach(p => p.draw());
  requestAnimationFrame(animate);
}

setInterval(emitParticles, 4000)
emitParticles()
animate();
canvas {
  display: block;
  margin: 0 auto;
  background: #000;
  /* Black background */
}
<canvas id="fountainCanvas"></canvas>

// script.js

const container = document.getElementById("waterfall-container");

function createFallingElement() {
    // Create a new div element
    const waterfallItem = document.createElement("div");
    waterfallItem.classList.add("waterfall-item");

    // Set a random horizontal position
    const randomX = Math.random() * window.innerWidth;
    waterfallItem.style.left = `${randomX}px`;

    // Append the element to the container
    container.appendChild(waterfallItem);

    // Remove the element after animation ends to avoid clutter
    waterfallItem.addEventListener("animationend", () => {
        container.removeChild(waterfallItem);
    });
}

// Create new falling elements every 200ms
setInterval(createFallingElement, 200);

本文标签: javascriptHow to create a waterfall effectStack Overflow