2016-07-30 44 views
1

我是一個畫布初學者,對不起,如果這是一個微不足道的問題。如何讓我工作中的煙花爆炸後淡出?畫布淡出粒子

https://jsfiddle.net/ccwhryvv/

var SCREEN_WIDTH = window.innerWidth, 
SCREEN_HEIGHT = window.innerHeight, 
mousePos = { 
    x: 400, 
    y: 300 
}, 

// create canvas 
canvas = document.createElement('canvas'), 
context = canvas.getContext('2d'), 
particles = [], 
rockets = [], 
MAX_PARTICLES = 400, 
colorCode = 0; 

// init 
$(document).ready(function() { 
    $('#content')[0].appendChild(canvas); 
    canvas.width = SCREEN_WIDTH; 
    canvas.height = SCREEN_HEIGHT; 
    setInterval(launch, 800); 
    setInterval(loop, 1000/50); 
}); 


// update mouse position 
$(document).mousemove(function(e) { 
    e.preventDefault(); 
    mousePos = { 
     x: e.clientX, 
     y: e.clientY 
    }; 
}); 

// launch more rockets!!! 
$(document).mousedown(function(e) { 
    for (var i = 0; i < 5; i++) { 
     launchFrom(Math.random() * SCREEN_WIDTH * 2/3 + SCREEN_WIDTH/6); 
    } 
}); 

function launch() { 
    launchFrom(SCREEN_WIDTH/2); 
} 

function launchFrom(x) { 
    if (rockets.length < 10) { 
     var rocket = new Rocket(x); 
     rocket.explosionColor = Math.floor(Math.random() * 360/10) * 10; 
     rocket.vel.y = Math.random() * -3 - 4; 
     rocket.vel.x = Math.random() * 6 - 3; 
     rocket.size = 8; 
     rocket.shrink = 0.999; 
     rocket.gravity = 0.01; 
     rockets.push(rocket); 
    } 
} 

function loop() { 
    // update screen size 
    if (SCREEN_WIDTH != window.innerWidth) { 
     canvas.width = SCREEN_WIDTH = window.innerWidth; 
    } 
    if (SCREEN_HEIGHT != window.innerHeight) { 
     canvas.height = SCREEN_HEIGHT = window.innerHeight; 
    } 

    // clear canvas 
    context.fillStyle = "rgba(0, 0, 0, 0.0)"; 
    context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); 

    var existingRockets = []; 

    for (var i = 0; i < rockets.length; i++) { 
     // update and render 
     rockets[i].update(); 
     rockets[i].render(context); 

     // calculate distance with Pythagoras 
     var distance = Math.sqrt(Math.pow(mousePos.x - rockets[i].pos.x, 2) + Math.pow(mousePos.y - rockets[i].pos.y, 2)); 

     // random chance of 1% if rockets is above the middle 
     var randomChance = rockets[i].pos.y < (SCREEN_HEIGHT * 2/3) ? (Math.random() * 100 <= 1) : false; 

/* Explosion rules 
      - 80% of screen 
      - going down 
      - close to the mouse 
      - 1% chance of random explosion 
     */ 
     if (rockets[i].pos.y < SCREEN_HEIGHT/5 || rockets[i].vel.y >= 0 || distance < 50 || randomChance) { 
      rockets[i].explode(); 
     } else { 
      existingRockets.push(rockets[i]); 
     } 
    } 

    rockets = existingRockets; 

    var existingParticles = []; 

    for (var i = 0; i < particles.length; i++) { 
     particles[i].update(); 

     // render and save particles that can be rendered 
     if (particles[i].exists()) { 
      particles[i].render(context); 
      existingParticles.push(particles[i]); 
     } 
    } 

    // update array with existing particles - old particles should be garbage collected 
    particles = existingParticles; 

    while (particles.length > MAX_PARTICLES) { 
     particles.shift(); 
    } 
} 

function Particle(pos) { 
    this.pos = { 
     x: pos ? pos.x : 0, 
     y: pos ? pos.y : 0 
    }; 
    this.vel = { 
     x: 0, 
     y: 0 
    }; 
    this.shrink = .97; 
    this.size = 2; 

    this.resistance = 1; 
    this.gravity = 0; 

    this.flick = false; 

    this.alpha = 1; 
    this.fade = 0; 
    this.color = 0; 
} 

Particle.prototype.update = function() { 
    // apply resistance 
    this.vel.x *= this.resistance; 
    this.vel.y *= this.resistance; 

    // gravity down 
    this.vel.y += this.gravity; 

    // update position based on speed 
    this.pos.x += this.vel.x; 
    this.pos.y += this.vel.y; 

    // shrink 
    this.size *= this.shrink; 

    // fade out 
    this.alpha -= this.fade; 
}; 

Particle.prototype.render = function(c) { 
    if (!this.exists()) { 
     return; 
    } 

    c.save(); 

    c.globalCompositeOperation = 'lighter'; 

    var x = this.pos.x, 
     y = this.pos.y, 
     r = this.size/2; 

    var gradient = c.createRadialGradient(x, y, 0.1, x, y, r); 
    gradient.addColorStop(0.1, "rgba(255,255,255," + this.alpha + ")"); 
    gradient.addColorStop(0.8, "hsla(" + this.color + ", 100%, 50%, " + this.alpha + ")"); 
    gradient.addColorStop(1, "hsla(" + this.color + ", 100%, 50%, 0.1)"); 

    c.fillStyle = gradient; 

    c.beginPath(); 
    c.arc(this.pos.x, this.pos.y, this.flick ? Math.random() * this.size : this.size, 0, Math.PI * 2, true); 
    c.closePath(); 
    c.fill(); 

    c.restore(); 
}; 

Particle.prototype.exists = function() { 
    return this.alpha >= 0.1 && this.size >= 1; 
}; 

function Rocket(x) { 
    Particle.apply(this, [{ 
     x: x, 
     y: SCREEN_HEIGHT}]); 

    this.explosionColor = 0; 
} 

Rocket.prototype = new Particle(); 
Rocket.prototype.constructor = Rocket; 

Rocket.prototype.explode = function() { 
    var count = Math.random() * 10 + 80; 

    for (var i = 0; i < count; i++) { 
     var particle = new Particle(this.pos); 
     var angle = Math.random() * Math.PI * 2; 

     // emulate 3D effect by using cosine and put more particles in the middle 
     var speed = Math.cos(Math.random() * Math.PI/2) * 15; 

     particle.vel.x = Math.cos(angle) * speed; 
     particle.vel.y = Math.sin(angle) * speed; 

     particle.size = 10; 

     particle.gravity = 0.2; 
     particle.resistance = 0.92; 
     particle.shrink = Math.random() * 0.05 + 0.93; 

     particle.flick = true; 
     particle.color = this.explosionColor; 



     particles.push(particle); 
    } 
}; 

Rocket.prototype.render = function(c) { 
    if (!this.exists()) { 
     return; 
    } 

    c.save(); 

    c.globalCompositeOperation = 'lighter'; 

    var x = this.pos.x, 
     y = this.pos.y, 
     r = this.size/2; 

    var gradient = c.createRadialGradient(x, y, 0.1, x, y, r); 
    gradient.addColorStop(0.1, "rgba(255, 255, 255 ," + this.alpha + ")"); 
    // gradient.addColorStop(1, "rgba(255, 255, 255, " + this.alpha + ")"); 
    gradient.addColorStop(1, "rgba(255, 255, 255, 0)"); 

    c.fillStyle = gradient; 

    c.beginPath(); 
    c.arc(this.pos.x, this.pos.y, this.flick ? Math.random() * this.size/2 + this.size/2 : this.size, 0, Math.PI * 2, true); 
    c.closePath(); 
    c.fill(); 

    c.restore(); 
}; 

謝謝!

回答

0

創建漸變很昂貴 - 尤其是在動畫循環中。

這是更有效的爲您的應用程序啓動之前預先建立梯度爆炸精靈的spritesheet:

  • 創建一個內存中的畫布來充當spritesheet,
  • 選擇了十幾個標準顏色你爆炸了。
  • 按照爆炸順序創建梯度精靈。

var canvas=document.getElementById("canvas"); 
 
var ctx=canvas.getContext("2d"); 
 
var cw=canvas.width; 
 
var ch=canvas.height; 
 

 
var ss=makeSpritesheet(10,15); 
 

 
ctx.fillStyle='navy'; 
 
ctx.fillRect(0,0,cw,ch); 
 
ctx.drawImage(ss,0,0); 
 

 
function makeSpritesheet(maxRadius,colorCount){ 
 
    var c=document.createElement('canvas'); 
 
    var ctx=c.getContext('2d'); 
 
    var spacing=maxRadius*2.5; 
 
    c.width=spacing*maxRadius; 
 
    c.height=spacing*(colorCount+1); 
 
    for(var colors=0;colors<colorCount;colors++){ 
 
     var y=(colors)*spacing+spacing/2; 
 
     var color = parseInt(colors/colorCount*360); 
 
     for(r=2;r<=maxRadius;r++){ 
 
      var x=(r-1)*spacing; 
 
      var gradient = ctx.createRadialGradient(x, y, 0, x, y, r); 
 
      gradient.addColorStop(0.2, "white"); 
 
      gradient.addColorStop(0.7, 'hsla('+color+', 100%, 50%, 1)'); 
 
      gradient.addColorStop(1.0, "rgba(0,0,0,0)"); 
 
      ctx.fillStyle = gradient; 
 
      ctx.beginPath(); 
 
      ctx.arc(x,y,r,0,Math.PI*2); 
 
      ctx.closePath(); 
 
      ctx.fill(); 
 
     } 
 
    } 
 
    return(c); 
 
}
body{ background-color:white; } 
 
#canvas{border:1px solid red; }
<canvas id="canvas" width=640 height=512></canvas>

動畫過程中,借鑑了spritesheet精靈到你的畫布。

通過在繪製每個精靈之前設置context.globalAlpha來淡化每個精靈的不透明度。