Javascript: Confetti particles falling down in canvas

╄→гoц情女王★ 提交于 2019-12-11 07:54:26

问题


I found this great script to make it rain confetti: http://jsfiddle.net/hcxabsgh/ Note how the particles have this twist/turn/skew-effect to make it feel more natural.

I have been trying to make the particles round which also works nicely: http://jsfiddle.net/rqr9hb7x/2/

But i can't get the particles to twist/turn/skew around their axes like in the rectangular example.

I figured it should be doable with ctx.setTransform(), since it handles skew-parameters, but it seems to skew the whole canvas instead of the single particle. Anyone an idea on how to approach this properly?

this.draw = function () {
    ctx.beginPath();
    ctx.save();
    //ctx.setTransform(1, 0, 0, 1, 0, 0); // straigt
    ctx.setTransform(1, .3, 0, .7, 0, 0); // skewed - to make dynamic
    ctx.arc(this.x + this.tilt, this.y + this.tilt + (this.r / 4), (this.r / 2), 0, Math.PI * 2, false);
    ctx.restore();
    ctx.fillStyle = this.color;
    return ctx.fill();
}

回答1:


Skew using setTransform

You want to rotate the particles local x axis over time. The x axis is the first two arguments of setTransform. To rotate that axis over time you use cos(angle) and sin(angle) to set the x and y component of the x axis.

As you want that to be local to the particle you need to also set the particle origin (point of rotation) and then draw the particle at 0,0. The origin is the last two arguments of setTransform

The draw function you want is

draw() {
    ctx.fillStyle = this.color;
    ctx.beginPath();
    ctx.setTransform(
        Math.cos(this.tiltAngle),  // set the x axis to the tilt angle
        Math.sin(this.tiltAngle),
        0, 1, 
        this.x, this.y    // set the origin
    ); 
    ctx.arc(0, 0, (this.r / 2), 0, Math.PI * 2, false); // draw at origin (0,0)
    ctx.fill();
}

Example

Looking at the code at the fiddle given, i could not work out what they were trying to do as it was way over complicated, so I rewrote the whole thing which now takes a 3rd as much code and no jQuery.

Run demo for the FX you are after...

maybe????

(function () {
    requestAnimationFrame(mainLoop);
    const ctx = canvas.getContext("2d");
    var W,H;
    var confetti = new Particles();
    var droppedCount = 0;
    var particlesPerFrame = 1.5;
    var wind = 0;
    var windSpeed = 1; 
    const windSpeedMax = 1;
    const windChange = 0.01;
    const windPosCoef = 0.002;
    const maxParticlesPerFrame = 2; //max particles dropped per frame
    var id = 0;
    stopButton.addEventListener("click",() => particlesPerFrame = 0 );
    startButton.addEventListener("click",() => particlesPerFrame = maxParticlesPerFrame);
		const randI = (min, max = min + (min = 0)) => (Math.random() * (max - min) + min) | 0;
		const rand  = (min = 1, max = min + (min = 0)) => Math.random() * (max - min) + min;    
    const colors = {
        options: "DodgerBlue,OliveDrab,Gold,pink,SlateBlue,lightblue,Violet,PaleGreen,SteelBlue,SandyBrown,Chocolate,Crimson".split(","),
        index: 0,
        step: 10,
        get color() { return colors.options[((colors.index++) / colors.step | 0) % colors.options.length] }
    }
    function Confetti() { this.setup() }
    Confetti.prototype = {
        setup(){        
            this.x = rand(-35,W + 35);
            this.y = rand(-30,-35);
            this.r = rand(10, 30); 
            this.d = rand(150) + 10; //density;
            this.color = colors.color;
            this.tilt = randI(10);
            this.tiltAngleIncremental = (rand(0.08) + 0.04) * (rand() < 0.5 ? -1 : 1);
            this.tiltAngle = 0;
            this.angle = rand(Math.PI * 2);
            this.id = id++;
            return this;
        },
        update() {
            this.tiltAngle += this.tiltAngleIncremental * (Math.cos(wind + (this.d + this.x + this.y) * windPosCoef) * 0.2 + 1);
            this.y += (Math.cos(this.angle + this.d) + 3 + this.r / 2) / 2;
            this.x += Math.sin(this.angle);
            this.x += Math.cos(wind + (this.d + this.x + this.y) * windPosCoef) * windSpeedMax;
            this.y += Math.sin(wind + (this.d + this.x + this.y) * windPosCoef) * windSpeedMax;
            this.tilt = (Math.sin(this.tiltAngle - (this.id / 3))) * 15;
            return this.y > H;  // returns true if particle is past bottom
        },
        draw() {
            ctx.fillStyle = this.color;
            ctx.beginPath();
            ctx.setTransform(
                Math.cos(this.tiltAngle),  // set the x axis to the tilt angle
                Math.sin(this.tiltAngle),
                0, 1,  
                this.x, this.y    // set the origin
            ); 
            ctx.arc(0, 0, (this.r / 2), 0, Math.PI * 2, false);
            ctx.fill();
        }
    }
    function Particles() {
        const items = [];
        const pool = [];
        this.update = function() {
            for (var i = 0; i < items.length; i++) {
                if(items[i].update() === true){ pool.push(items.splice(i--,1)[0]) }
            }            
        }
        this.draw = function() { for (var i = 0; i < items.length; i++) { items[i].draw()  } }
        this.add = function() {
            if (pool.length > 0) { items.push(pool.pop().setup()) }
            else { items.push(new Confetti()) }
        }
    }
    function mainLoop(time) {
        if (W !== innerWidth || H !== innerHeight) {
            W = canvas.width = innerWidth;
            H = canvas.height = innerHeight;
        } else {
            ctx.setTransform(1,0,0,1,0,0);
            ctx.clearRect(0, 0, W, H);
        }
        windSpeed = Math.sin(time / 8000) * windSpeedMax;
        wind += windChange;
        while(droppedCount < particlesPerFrame){
           droppedCount += 1;
           confetti.add();
        }
        droppedCount -= particlesPerFrame;
        confetti.update();
        confetti.draw();
        requestAnimationFrame(mainLoop);
    }
})();
* {
  margin: 0;
  padding: 0;
}

body {
  /*You can use any kind of background here.*/
  background: transparent;
}

canvas {
  display: block;
  position: relative;
  zindex: 1;
  pointer-events: none;
}

#content {
  text-align: center;
  width: 500px;
  height: 300px;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-left: -250px;
  margin-top: -150px;
  color: silver;
  font-family: verdana;
  font-size: 45px;
  font-weight: bold;
}

.buttonContainer {
  display: inline-block;
}

button {
  padding: 5px 10px;
  font-size: 20px;
}
<div id="content">
  Confetti World
  <br /> I 💙 confetti!
  <br />
  <div class="buttonContainer">
    <button id="stopButton">Stop Confetti</button>
    <button id="startButton">Drop Confetti</button>
  </div>
</div>
<canvas id="canvas"></canvas>

Note Some of this code and content has been copied from this fiddle.



来源:https://stackoverflow.com/questions/46890767/javascript-confetti-particles-falling-down-in-canvas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!