キャンバスをいじりすぎると、自動的にパフォーマンスの問題を考慮し始めるようになります。キャンバスアニメーションを最適化するにはどうすればよいですか?
【キャッシュを使用する】
キャッシュを使用するということは、事前レンダリングにオフスクリーン キャンバスを使用することを意味します。原理は非常に簡単です。つまり、最初にオフスクリーン キャンバスに描画し、次に、drawImage を通じてオフスクリーン キャンバスをメイン キャンバスに描画します。誤解している人も多いかもしれませんが、これはゲームでよく使われるダブルバッファリングの仕組みではないでしょうか。
実際、ゲームプログラミングでは画面のちらつきを防ぐためにダブルバッファリングメカニズムが使用されているため、描画時には、最初に画面のコンテンツが描画されます。背景キャンバスが描画され、キャンバス内のデータが前面キャンバスに描画されます。これはダブル バッファリングですが、最新のブラウザには基本的にダブル バッファリング メカニズムが組み込まれているため、キャンバスにはダブル バッファリングはありません。したがって、オフスクリーン キャンバスの使用はダブル バッファリングではなく、オフスクリーン キャンバスをキャッシュ領域として扱います。繰り返し描画する必要がある画面データをキャッシュして、キャンバス API の呼び出しの消費を削減します。
ご存知のとおり、キャンバス API の呼び出しはパフォーマンスを消費します。そのため、繰り返し画面データを描画する場合は、オフスクリーン キャンバスを適切に使用するとパフォーマンスが大幅に向上します。
1. キャッシュは使用されません
2. キャッシュは使用されますが、オフスクリーンキャンバスの幅と高さが設定されていません
3. キャッシュは使用されますが、オフスクリーン キャンバスの幅と高さは設定されていません
4. キャッシュを使用し、オフスクリーン キャンバスの幅と高さを設定します
上記のデモのパフォーマンスが異なることがわかります。以下の理由を分析してみましょう。 キャッシュが有効になっていない場合、各円のスタイルを実現するために、ループ描画を使用しました。ページ上の円がある一定のレベルになると、アニメーションの各フレームごとに大量の Canvas API 呼び出しが必要になり、大量の計算が必要になるため、どんなに優れたブラウザーを使用しても、下。
XML/HTML コードコンテンツをクリップボードにコピー
- ctx.save();
-
var j=0;
-
ctx.lineWidth = borderWidth;
-
for(var i=1;i<this.r;i =borderWidth){
- ctx.beginPath();
- ctx.ストロークスタイル = this.color[j];
- ctx.arc(this.x , this.y , i , 0 , 2*Math.PI);
- ctx.ストローク();
- j ;
- }
- ctx.restore();
したがって、私たちの方法は非常に単純で、各フレームのオブジェクトの領域が別の 1 つのスクリーンキャンバスとして機能します。
スクリーンキャンバスを保存として作成した場合を除き、次のコード内に一点很关键があり、スクリーンキャンバスのサイズと高さを設定する必要があり、キャンバス生成後のサイズは300X150です。来来のオブジェクトの半径が最大でも 80 を超えないため、300X150 のサイズでは空き領域が多くなり、リソースの浪費が生じるため、スクリーン キャンバスのサイズと高さを下に配置する必要があり、保存されている要素のサイズが一致しますこれは、アニメーション パフォーマンスの向上にも役立ちます。上の 4 つのデモでは、パフォーマンスの差が示されています。 1000 個の仮想オブジェクトもトークンを取得できません。
XML/HTML コード
复制コンテンツ到剪贴板
- var ball = function(x , y , vx , vy , useCache){
- this.x = x;
- this.y = y;
- this.vx = vx;
- this.vy = vy;
- this.r = getZ(getRandom(20,40));
- this.color = [];
- this.cacheCanvas = document.createElement("canvas");
- thisthis.cacheCtx = this.cacheCanvas.getContext("2d");
- this.cacheCanvas.width = 2*this.r;
- this.cacheCanvas.height = 2*this.r;
- var num = getZ(this.r/borderWidth);
- for(var j=0;j<num;j ){
- this.color.push("rgba(" getZ(getRandom(0,255)) "," getZ(getRandom(0,255)) "," getZ(getRandom(0,255)) ",1)");
- }
- this.useCache = useCache;
- if(useCache){
- this.cache();
- }
- }
円オブジェクトをインスタンス化するとき、キャッシュ メソッドを直接呼び出し、複雑な円を円オブジェクトのオフスクリーン キャンバスに直接描画して保存します。
XML/HTML コードコンテンツをクリップボードにコピー
- cache:function(){
- this.cacheCtx.save();
- var j=0;
- this.cacheCtx.lineWidth = borderWidth;
for(var
i- =1;i<this.r;i =borderWidth){
this.cacheCtx.beginPath();
-
this- this.cacheCtx.ストロークスタイル = this.color[j];
this.cacheCtx.arc(this.r, this.r, i, 0, 2*Math.PI);
this.cacheCtx.ストローク(); -
j -
- this.cacheCtx.restore();
-
-
次のアニメーションでは、円オブジェクトのオフスクリーン キャンバスをメイン キャンバスに描画するだけで済みます。このように、各フレームで呼び出される CanvasAPI には次の文のみが含まれます。
- XML/HTML コードコンテンツをクリップボードにコピー
ctx.drawImage(this.cacheCanvas , this.x-this.r , this.y-this.r);
以前の for ループ描画と比較すると、非常に高速です。そのため、ベクター グラフィックスを繰り返し描画したり、複数の画像を描画したりする必要がある場合、オフスクリーン キャンバスを合理的に使用して画像データを事前にキャッシュすることができ、後続の各フレームでの不必要なパフォーマンスの消費を大幅に削減できます。
- 1000 個の円オブジェクトのスムーズ バージョン コードは以下に掲載されています:
XML/HTML コード
コンテンツをクリップボードにコピー
- >
-
<html lang="en" >
- <頭>
-
<メタ charset="UTF- 8">
- <スタイル>
- body{
- パディング:0;
- マージン:0;
- オーバーフロー: 非表示。
- }
- #cas{
- 表示: ブロック;
- 背景色:rgba(0,0,0,0);
- マージン:自動;
- border:1px solid;
- }
-
スタイル>
-
<タイトル>テストタイトル>
- 頭>
-
<ボディ>
- <div >
-
<キャンバス id='cas' 幅="800" 高さ="600">ブラウザはキャンバスキャンバスをサポートしていません>
- <div style="text- align:center">1000 個の円オブジェクトはスタックしませんdiv>>
-
div>
-
- <スクリプト>
-
var testBox = function(){
-
var キャンバス = ドキュメント.getElementById("cas"),
-
ctx = canvas.getContext('2d'),
-
borderWidth = 2,
-
ボール = [];
var -
ball = function(x, y, vx, vy, useCache){
-
this.x = x;
this.y-
= y;
this.vx
= -
this.vy = vy;
-
this.r = getZ(getRandom(20,40));
-
this.color = [];
-
this.cacheCanvas = document.createElement("canvas");
-
thisthis.cacheCtx = this.cacheCanvas.getContext("2d");
-
this.cacheCanvas.width = 2*this.r;
-
this.cacheCanvas.height = 2*this.r;
-
var num = getZ(this.r/borderWidth);
-
for(var j=0;j<num;j ){
- this.color.push("rgba(" getZ(getRandom(0,255)) "," getZ(getRandom(0,255)) "," getZ(getRandom(0,255)) ",1)");
- }
- this.useCache = useCache;
- if(useCache){
- this.cache();
- }
- }
-
- function getZ(num){
- var 四捨五入。
- 四捨五入 = (0.5 num) | 0;
- // double ビット単位ではありません。
- 四捨五入 = ~~ (0.5 num);
- // 最後に、左 ビット単位のシフトです。
- 四捨五入 = (0.5 num) << 0;
-
- 四捨五入して返します。
- }
-
- ball.prototype = {
- paint:function(ctx){
- if(!this.useCache){
- ctx.save();
- var j=0;
- ctx.lineWidth = borderWidth;
- for(var i=1;i<this.r;i =borderWidth){
- ctx.beginPath();
- ctx.ストロークスタイル = this.color[j];
- ctx.arc(this.x , this.y , i , 0 , 2*Math.PI);
- ctx.ストローク();
- j ;
- }
- ctx.restore();
- } else{
- ctx.drawImage(this.cacheCanvas , this.x-this.r , this.y-this.r);
- }
- },
-
- cache:function(){
- this.cacheCtx.save();
- var j=0;
- this.cacheCtx.lineWidth = borderWidth;
- for(var i=1;i<this.r;i =borderWidth){
- this.cacheCtx.beginPath();
- thisthis.cacheCtx.ストロークスタイル = this.color[j];
- this.cacheCtx.arc(this.r , this.r , i , 0 , 2*Math.PI);
- this.cacheCtx.ストローク();
- j ;
- }
- this.cacheCtx.restore();
- }、
-
- move:function(){
- this.x = this.vx;
- this.y = this.vy;
- if(this.x>(canvas.width-this.r)||this.x< this.r){
- thisthis.x=this.x<this.r?this.r:(canvas.width-this.r);
- this.vx = -this.vx;
- }
- if(this.y>(canvas.height-this.r)||this.y< this.r){
- thisthis.y=this.y<this.r?this.r:(canvas.height-this.r);
- this.vy = -this.vy;
- }
-
- this.paint(ctx);
- }
- }
-
- var ゲーム = {
- init:function(){
- for(var i=0;i<1000;i ){
- var b = new ball(getRandom(0,canvas.width) , getRandom(0,canvas) .height) , getRandom(-10 , 10) , getRandom(-10 , 10) , true)
- Balls.push(b);
- }
- }、
-
- update:function(){
- ctx.clearRect(0,0,canvas.width,canvas.height);
- for(var i=0;i<Balls.length;i ){
- Balls[i].move();
- }
- },
-
- loop:function(){
- var _this = this;
- this.update();
- RAF(function(){
- _this.loop();
- })
- }、
-
- start:function(){
- this.init();
- this.loop();
- }
- }
-
- window.RAF = (function(){
- return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {window.setTimeout(callback, 1000 / 60); };
- })();
-
- ゲームに戻る;
- }();
-
- function getRandom(a , b){
- return Math.random()*(b-a) a;
- }
-
- window.onload = function(){
- testBox.start();
- }
- スクリプト>
-
ボディ>
-
html>
オフスクリーン キャンバスに関する別の注意事項があります。オブジェクトの作成と破棄を継続的に行う場合は、少なくとも上で書いたように各オブジェクトの属性をバインドしないように注意してください。オフスクリーンキャンバスを設定します。
このようにバインドすると、オブジェクトが破棄されるとオフスクリーン キャンバスも破棄され、大量のオフスクリーン キャンバスが常に作成および破棄されるため、キャンバス バッファーが消費されます。 GPU リソースが大量にあるため、ブラウザがクラッシュしたり深刻なフレーム フリーズが発生しやすくなります。解決策は、オフスクリーン キャンバス配列を作成し、十分な数のオフスクリーン キャンバスをプリロードし、まだ生きているオブジェクトのみをキャッシュし、オブジェクトが破棄されたときにキャッシュを解除することです。これによってオフスクリーンのキャンバスが破壊されることはありません。
【requestAnimationFrameを使用する】
これについては詳しく説明しませんが、setTimeout や setInterval ではなく、これがアニメーションに最適なループであることは多くの人が知っていると思います。互換性の記述方法を直接投稿します:
XML/HTML コードコンテンツをクリップボードにコピー
- window.RAF = (function(){
- window.requestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || 関数 (コールバック) {window.setTimeout(callback, 1000 / 60 );
- })();
【浮動小数点演算を避ける】
JavaScript には Math.floor、Math.ceil、parseInt などの非常に便利な丸めメソッドがいくつか用意されていますが、外国人の友人がテストを行っており、parseInt メソッドは追加の作業 (データが有効な値かどうかの検出、parseInt など) を実行します。最初にパラメータを文字列に変換することもできます。) したがって、parseInt を直接使用すると、比較的パフォーマンスに負荷がかかります。したがって、切り上げ方法としては、外国人が作成した非常に賢いメソッドを直接使用できます。
JavaScript コードコンテンツをクリップボードにコピーします
1.rounded = (0.5 somenum) 0;
2.rounded = ~~ (0.5 somenum)
演算子がわからない場合は、ここをクリックしてください: http://www.w3school.com.cn/js/pro_js_operators_bitwise.asp 内に詳細な説明があります
【canvasAPI呼び出しを可能な限り減らす】
パーティクル エフェクトを作成するときは、円をできるだけ使用せず、できれば正方形を使用してください。パーティクルが小さすぎるため、正方形は円に似てしまいます。理由としては、円を描くには 3 つのステップが必要であることが分かります。まず beginPath を使用して円弧を描き、次に fill を使用してそれを塗りつぶして円を作成します。ただし、正方形を描画するには、fillRect が 1 つだけ必要です。たった2回の呼び出しの差ですが、パーティクルオブジェクトの数が一定レベルに達すると、パフォーマンスの差が現れます。
他にも注意すべき点がいくつかありますが、Google にはかなりの数の情報があるため、すべてをリストすることはしません。これは主にキャッシュの使用状況を記録するための自分用の記録と言えます。 Canvas のパフォーマンスを向上させたい場合、最も重要なことは、コードの構造に注意を払い、不要な API 呼び出しを減らし、各フレームの複雑な操作を減らすか、複雑な操作を各フレームに 1 回から複数のフレームに 1 回に変更することです。フレーム。同時に、上記のキャッシュの使用では、便宜上、各オブジェクトにオフスクリーン キャンバスを使用しました。実際には、オフスクリーン キャンバスを使用しすぎると、オフスクリーン キャンバスを広範囲に使用できなくなります。パフォーマンス上の問題が発生します。オフスクリーン キャンバスを適切に使用してください。
ソースコードのアドレス:
https://github.com/whxaxes/canvas-test/tree/gh-pages/src/Other-demo/cache