我希望我的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;
是否可以在没有帆布填充颜色的情况下实现蒸汽效果?
答案 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
我已经更新了答案,包括我错过的图片加载。您可以在下面的脚本中找到所有详细信息,因为我已经评论了我添加和更改的所有内容。
"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;