キャンバス上のリアルな煙。

はじめに



インターネットには、煙の効果を出す方法に関するいくつかの記事がありますが、スクリプトはあまりにも「重く」、個人的には完全に明確ではありません。 そこで、そのような効果をプロジェクトに実装することに興味がある人のために、タスクを簡素化することにしました。

私はあまり記述しませんが、ほとんどは詳細なコメント付きのコードです



ページレイアウト:



<html> <head> <title>smoke</title> <meta charset="utf-8"/> </head> <body bgcolor = "#000"> <div id = "info" style = "color: #fff"></div> <canvas id = "canvas" width = "500px" height = "500px"></canvas> <script> ... </script> </body> </html>
      
      





これですべてが明確になりました。 Canvas、パーティクルの数を表示するdiv、および黒の背景を持つボディがあります。白の背景では、効果はそれほど美しくありません。



ピースごとのスクリプト:



まず、ページ上の必要な要素を見つけて、ランダムな値を取得するための便利な関数を作成する必要があります。

 info = document.getElementById('info'); canvas = document.getElementById('canvas'); ctx = canvas.getContext('2d'); function random(min,max){ return Math.random() * (max - min) + min; }
      
      





その後、パーティクルの配列、パーティクルを追加する機能を作成する必要があります(私のコードでは、これはコンストラクタ=であると書くようにはなりません)。 実際、パーティクルには次のプロパティがあります。



また、これらの各プロパティには、このプロパティを変更する値があります(トートロジーについてはごめんなさい)。

各パーティクルはオブジェクトにすぎず、このオブジェクトはパーティクル配列の要素です。

 function addParticle(){ particles.push({ num: particles.length, // op: random(0,0.5), // dop: random(0.002,0.008), //   r: random(3,10), // dr: random(0.5,1.5), //   a: random(0,180), // da: random(-3,3), //   x: random(249,251), //- dx: random(-0.2,0.2), //    y: random(349,351), //- dy: random(0.5,1), //    draw: /*       */ }); }
      
      





上記のコードでは、すべてが明確だと思います。 これで、楽しい部分がレンダリングされます。

テクノロジーは次のとおりです。各パーティクルは、すべての新しいパラメーター(座標、半径など)を個別に計算します。 その後、キャンバスの透明度が変わります(ctx.globalAlpha = ...)。 さらに、最適化のために、前回のレンダリングから経過した時間をチェックし、この時間がフレーム時間よりも短い場合はレンダリングが実行され、そうでない場合は、パラメーター計算のみが実行されるため、パーティクルが大きな負荷で静止しないようになります。 つまり、このような自己記述フレームスキップシステムが判明します。 さて、レンダリング自体には、パーティクルの正しい回転のための初期座標の変更(ctx.translate)、X座標とY座標の周りのキャンバスの直接回転(ctx.rotate(ラジアン単位の角度))、およびレンダリング(ctx.drawImage)が含まれます。 座標に対して画像を中央に配置し、「半径の半分」で描画する必要があることに注意してください。また、度をラジアンに変換することも忘れないでください(角度* Math.PI / 180)。

 draw: function(timer){ this.op -= this.dop; //   if(this.op > 0){ //   0,       ,     ? this.r += this.dr; //  this.a -= this.da; //  ( !) this.x -= this.dx; // - this.y -= this.dy; // - ctx.globalAlpha = this.op; //   if(window.performance.now() - time < 28){ //    "",      ,      ctx.save(); //   ctx.translate(this.x,this.y); //   ctx.rotate(this.a*Math.PI/180); //  ctx.drawImage(img,-this.r/2,-this.r/2,this.r,this.r); //  ctx.restore(); //    } } }
      
      





このコードで、すべてが非常に明確に説明されていることを願っています。

次の部分は、すべてのパーティクルのパス関数です。 最初に呼び出し時間(window.performance.now())が設定され、上記の関数を使用して新しいパーティクルが追加され、キャンバスが消去され、サイクル内の各パーティクルの「実行」があります。 同時に、パーティクルの数を自動的に制御するために、パーティクルのパラメータがレンダリング中に見えないような場合(透明度ゼロ、キャンバスを超えて)、配列要素(パーティクル)を削除します。 この関数は再帰的であり、すべてのレンダリング後に自分自身を呼び出します。 requestAnimationFrameを使用しない理由を尋ねますか? 煙効果自体はオンラインゲームで使用されている(ほぼ終了している)と答え、requestAnimationFrameを使用すると、別のブラウザータブに切り替えたときに関数が呼び出されなくなり、したがってすべての計算が実行されなくなります。まだゲームを開きません=)。 実際のコード:

 function draw(){ time = window.performance.now(); //   addParticle(); //  info.innerHTML = particles.length; //  div   canvas.width = canvas.width; //  for(var i = 0; i < particles.length; i++){ //   ,      if(particles[i].op <= 0 || particles[i].x < -100 || particles[i].x > 600 || particles[i].y < -100){ particles[i].op = 0; particles[i] = null; delete particles[i]; particles.splice(i,1); } particles[i].draw(); //    .       else,         ,      "" } setTimeout(draw, 30); //... }
      
      





コードのつづりはこれで終わりです。 画像を初期化し、「パーティクルパス」関数への最初の呼び出しを実行するためにのみ残ります。 ところで、ここに写真があります:

画像

そして、ここにコードがあります:

 img = new Image(); img.src = 'smoke.png'; img.onload = draw();
      
      





ライブリンク -わかりやすくするために、さまざまなブラウザーで、わかりやすくするためにwindow.performance.now()をDate.now()に変更しました。 これで、より大きなデバイスで動作するはずです。

PS正直に言って、もしうまくいかない場合は、Chromeでのみ作業を確認しました。

ソース-F12。

何が良いのか、何が悪いのかを決定するのに十分な批判を待っていますが、すべての行が明確で正しいことを願っています。 またね!



All Articles