2016-06-14 23 views
0

キャンバススチームエフェクトを使用して残りのページコンテンツをオーバーレイしたいと思っていますが、キャンバスに塗りつぶしを与えないとスチームがポスタリゼーション。HTML5キャンバススチームエフェクトがキャンバスフィリングなしで正しく表示されない

コメントなし行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);
body{ 
 
    background:green; 
 
} 
 
canvas { 
 
    border: 1px dotted black; 
 
}

それは無いキャンバス塗りつぶしの色でスチーム効果を達成することは可能ですか?

+1

使用(H、W 0,0、) '' ctx.clearRectではなく、fillRect、あなたは、あなたが望む透明キャンバスを取得します。 – Blindman67

+0

それは何をしているのか分かりませんが、それは治療法です!ありがとうございました。 –

+0

私は、あなたが変更を行うことができるいくつかのビットがあるので、答えを出します。 – Blindman67

答えて

2

ctx.clearRect(0,0,w,h)を使用すると、必要な透明な背景が得られます。

私もいくつかの変更を加えました。

フレームごとに新しいパーティクルを作成すると、時間が経つにつれて多くのGCが発生します。既存のパーティクルをリセットしてパーティクルにリセット機能を追加し、新しいパーティクルを追加してカウンターでリセットするほうがよい。

トランスフォームの設定が非効率的なので、トランスフォームを直接設定することで簡単に追加できました。これで、各パーティクルのキャンバスの状態を保存して復元する必要がなくなり、多くのマシン/デバイスで遅くなる可能性があります。

私はまた少し時間を節約

、表示した画像を描画していないために何のためには低すぎるアルファ値のチェックをしたというよりも待っている原因のない単なるバグであるsetIntervalを使用しています遅い機械は痛みである。私は​​を追加しました。これは、画面のリフレッシュとブラウザのレンダリングに同期された素晴らしい60Fpsを提供します。

更新。

パーティクルアルファが閾値c = 1/255(カットオフのc)を下回るために必要なフレーム数に合わせて、パーティクルの数を減らすことができます。

あなたは時間tとして、各フレームのステップを考えると、我々はアルファ値までのフレーム数をしたいならば、それはとてもctx.alpha = a * Math.pow(d ,t)

のように表すことができる。あなたはいつもa = 0.4でアルファを開始し、減衰率はd = 0.9ですc

あなたが持っている腐敗の結果は44であり、456の配列エントリを無駄にしていたため、alpha = a*Math.pow(d,t)-cを解決する必要があります。

更新#2

私はbefor逃した画像の読み込みを含めるように答えを更新しました。私が追加して変更したすべてをコメントしたので、下のスクリプトですべての詳細を見つけることができます。

"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);
canvas { 
 
    border: 1px dotted black; 
 
}

+0

このような包括的な答えをありがとう!今はずっと効率的に走っています。私が持っている唯一の質問は、蒸気の後ろのキャンバスに別の静的PNG(https://pixabay.com/static/uploads/photo/2012/04/18/03/11/coffee-367​​26_960_720.png)を追加する方法ですそれを引き出し関数に追加することで、引き続き再描画されることなく、効果が得られますか? –

+0

@ PaulBenbow背景画像が必要な場合は、パーティクルをレンダリングする前に各フレームをレンダリングする必要があります。私はちょうどあなたがイメージをロードするが、それがロードされているかどうかをチェックしないことに気づいた。 image.srcの設定とイメージの準備が完了するまでに時間がかかるため、イメージが読み込まれるまでアニメーションを開始しないでください。私はこれをどのように行うことができるかを示すために答えを修正します。 – Blindman67

+0

ありがとうございます。背景画像を表示して、c.clearRect(0、0、w、h)を使用しないでください。私が持っていた元のポスタリゼーションの問題が発生し、clearRectを使用するとバックグラウンドが点滅します。背景を静的に保つ方法はありますか? –

関連する問題