没有画布填充的HTML5 Canvas蒸汽效果无法正确显示

时间:2016-06-14 16:01:18

标签: canvas html5-canvas

我希望我的Canvas蒸汽效果覆盖我的其余页面内容,但是如果我不给画布填充,它会以一种方式显示蒸汽。

取消评论第24行& 25将按预期填充画布和蒸汽显示。

c.fillStyle = '#000';
c.fillRect(0,0,w,h);

以下代码段的完整演示。



var canvas = document.createElement('canvas');
var w = canvas.width = 800;
var h = canvas.height = 700;
var c = canvas.getContext('2d');
var img = new Image();
img.src = "http://wightfield.com/_temp/smoke_600-60.png";
var position = {x : 450, y : 410};
var mugPosition = {x : w/3, y : 500};

document.body.appendChild(canvas);

var particles = [];
var random = function(min, max){
  return Math.random()*(max-min)*min;
};

var draw = function(){
  position.x;
  position.y; 
  var p = new Particle(position.x, position.y);
  particles.push(p);
  while(particles.length > 500) particles.shift();
  
  //c.fillStyle = '#000';
  //c.fillRect(0,0,w,h);

  for(var i = 0; i < particles.length; i++)
  {
    particles[i].update();
  }
};
// generates the smoke particles
function Particle(x, y){
  this.x = x;
  this.y = y;
  this.velX = (random(1, 10)-5)/10;
  // distance of vertical travel
  this.velY = -9;
  this.size = random(3, 6)/10;
  this.alpha = 0.4;
  this.update = function(){
    this.y += this.velY;
    this.x += this.velX;
    this.velY *= 0.99;
    if(this.alpha < 0)
      this.alpha = 0;
    c.globalAlpha = this.alpha;
    c.save();
    c.translate(this.x, this.y);
    c.scale(this.size, this.size);
    
    c.drawImage(img, -img.width/2, -img.height/2);
    c.restore();
    this.alpha *= 0.90;
    this.size += 0.015;//
  };
}

setInterval(draw, 800/16);
&#13;
body{
  background:green;
}
canvas {
  border: 1px dotted black;
}
&#13;
&#13;
&#13;

是否可以在没有帆布填充颜色的情况下实现蒸汽效果?

1 个答案:

答案 0 :(得分:2)

使用ctx.clearRect(0,0,w,h)获取所需的透明背景。

我也做了一些改变。

你在每一帧创建一个新粒子的地方,随着时间的推移会产生很多GC。最好重置一个现有的粒子,所以我给粒子添加了一个重置​​函数并添加了新的粒子,然后通过一个计数器重置它们。

您设置转换的效率很低,因此我通过直接设置转换添加了更快捷的方法。现在,您不必为每个粒子保存和恢复画布状态,这在许多机器/设备上可能很慢。

我还检查了一个太低的alpha值,无法显示任何内容而不能绘制图像,节省了一点时间

而不是使用setInterval这只是一个没有原因等待慢速机器成为一个痛苦的错误。我添加了requestAnimationFrame,它将为屏幕刷新和浏览器呈现提供一个非常流畅的60Fps。

更新

刚刚意识到粒子的数量可以减少,以适应粒子α低于c = 1/255阈值所需的帧数(c表示截止值)。

您始终在a = 0.4处启动Alpha,并且衰减率为d = 0.9如果您将每个帧步骤视为时间t,那么它可以表示为ctx.alpha = a * Math.pow(d ,t) < / p>

因此,如果我们想要在Alpha值低于c之前的帧数,我们需要为alpha = a*Math.pow(d,t)-c

解析t = Math.log(c/a)/Math.log(d)

你有腐烂的结果是44,所以你浪费了456个阵列条目。

更新#2

我已经更新了答案,包括我错过的图片加载。您可以在下面的脚本中找到所有详细信息,因为我已经评论了我添加和更改的所有内容。

&#13;
&#13;
"use strict"; // this is a javascript directive and must be on the first line of the 
              // script (if included but is not a requirement). 
              // It forces a more code run under more stringent rules. The advantages
              // are many, including making the code run faster.

var imageLoadCount = 0;  // counts the number of image loading, counts down as they load
var readyToAnimate = false; // flag to indicate that resources are available
// image indexes in images array to get correct images in the animation.
const PARTICLE_IMAGE_INDEX = 0;
const BACKGROUND_IMAGE_INDEX = 1;
var images = []; // an array of images 
// What follows is a self evoking function, this will isolate the loading stuff from the
// rest of the script as it is only needed once at start so no point keeping references to it all
// the self invoking function is
//  (function(){...code body})()
// the () at the end forces javascript to run what is inside the () before it.
(function (){        
    function imageLoaded(){ // image onload function "this" is a reference to the image
        imageLoadCount -= 1; // count the loaded image
        // if the count is zero all images have loaded
        if(imageLoadCount === 0){
            readyToAnimate = true;
        }
    }
    // a list of image urls that need to be loaded. 
    const imageURLs = [
        "http://wightfield.com/_temp/smoke_600-60.png",
        "http://wightfield.com/_temp/smoke_600-60.png", // repeating the image just as example
    ];
    imageURLs.forEach(function(url){  // for each image url start the load process
        var img = new Image();
        img.src = url;
        img.onload = imageLoaded; // set the image onload function
        imageLoadCount += 1;  // count the number of images loading
        images.push(img); 
    });
})();  // run the function
var canvas = document.createElement('canvas');
var w = canvas.width = 800;
var h = canvas.height = 700;
var c = canvas.getContext('2d');   
document.body.appendChild(canvas);    

var position = {
    x : 450,
    y : 410
};
var mugPosition = {
    x : w / 3,
    y : 500
};


var particles = [];
var random = function (min, max) {
    // YOU had Math.random() * (max - min) * min; I assume you did not want to multiply by min
    return Math.random() * (max - min) + min;
};
var particleCount = 0;
const ALPHA_CUTOFF = 1/255;
const ALPHA_START = 0.4;
const ALPHA_DECAY = 0.9
// calculate the number of particles need to show each step of the alpha decay
const MAX_PARTICLES = Math.ceil(Math.log(ALPHA_CUTOFF / ALPHA_START) / Math.log(ALPHA_DECAY));
console.log(MAX_PARTICLES)

var draw = function () {
    var i;
    if(readyToAnimate){  // wait for the resources to load 
        c.setTransform(1,0,0,1,0,0); // reset transform         
        c.clearRect(0, 0, w, h);
        // If you want to render a background image do it here. If the image is the size of the
        // canvas then there is no need to clear the canvas and you can delete the line above
        /*  As an example
        c.drawImage(images[BACKGROUND_IMAGE_INDEX],0,0,w,h); // draws image filling the canvas
        */
        if (particles[particleCount % MAX_PARTICLES] === undefined) {
            particles[particleCount % MAX_PARTICLES] = new Particle(position.x, position.y);
        } else {
            particles[particleCount % MAX_PARTICLES].reset(position.x, position.y);
        }
        particleCount += 1;

        for (i = 0; i < particles.length; i++) {
            particles[i].update();
        }
    }else{
        // if you wanted you can add loading progress here
    }
    requestAnimationFrame(draw);
};


function Particle(x, y) {
    this.reset(x, y);
}
Particle.prototype = {
    reset : function (x, y) {
        this.x = x;
        this.y = y;
        this.velX = (random(1, 10) - 5) / 10;
        this.velY = -9;
        this.size = random(3, 6) / 10;
        this.alpha = ALPHA_START ;
        this.image = images[PARTICLE_IMAGE_INDEX];
    },
    update : function () {
        if(this.alpha >= ALPHA_CUTOFF ){  // no point in rendering a invisible sprite
            this.y += this.velY;
            this.x += this.velX;
            this.velY *= 0.99;            
            c.globalAlpha = this.alpha;
            c.setTransform(this.size,0,0,this.size,this.x, this.y);
            c.drawImage(this.image, -this.image.width / 2, -this.image.height / 2);
            this.alpha *= ALPHA_DECAY ;
            this.size += 0.015; //
        }
    }
}
// start the animation. Images may not have loaded yet
requestAnimationFrame(draw);
&#13;
canvas {
  border: 1px dotted black;
}
&#13;
&#13;
&#13;