8

作成しているJavaScriptゲームの視覚効果を作成しようと取り組んでいますが、スプライトの色を調整するために使用しているコードによって、ブラウザーのメモリ使用量がどんどん増えていることに気付きました。一見無制限に見えます。

ここでコードとメモリリークを確認できます:http: //timzook.tk/javascript/test.html

このメモリリークは、新しいImageDataオブジェクトを再色付けするために、フレームごとに(setIntervalを介して)キャンバスコンテキストからgetImageDataを呼び出すときに、updateimage()関数でのみ発生します。javascriptのガベージコレクターが古いものを破壊すると思っていたでしょうが、そうでなければ、手動で破壊する方法がわかりません。なぜこれを行うのか、またはそれを修正する方法を理解するための助けをいただければ幸いです。

私の質問はこれに非常に似ています:getImageData、javascript、HTML5キャンバスのこの使用でメモリリークは何ですかしかし、setIntervalによって呼び出される関数内のすべてのフレームを実行するためのコードが必要です、setInterval関数の外に移動する彼のソリューションは私には選択肢がありません。彼がそれを解決する他の方法を見つけたかどうかを尋ねるコメントを残すことはできません。

テストする人への注意:この例ではgetImageDataを使用しているため、.htmlファイルにスローするだけではローカルでテストできません。Webサーバーが必要です。また、明らかにHTML5要素を使用しているため、一部のブラウザはそれを使用できません。

編集:*解決済み* ありがとう、以下の解決策で修正されました。drawImage()で画像を使用するのと同じようにcanvas要素を使用できることに気づかなかったので、コードを再構築して、メモリの使用量を大幅に減らしました。誰かが見たい場合は、この変更されたコードを上記のリンク先のページにアップロードしました。

4

1 に答える 1

12

への呼び出しからメモリリークが発生することはありませんgetImageData()。問題の原因は次の行です。

TempImg.src = ImgCanvas.toDataURL("image/png");

事実上、そのコード行が実行されるたびに、ブラウザは別の画像を「ダウンロード」してメモリに保存します。つまり、実際に最終的に得られるのは、非常に急速に成長しているキャッシュです。Chromeでサイトを開き、開発ツールの[リソース]タブを確認することで、これを簡単に確認できます(ctrl+shift+i)。

これを回避するには、ループTempImgCanvasを呼び出すたびに画像オブジェクトを更新し続ける代わりに、画像データを作成してそのキャンバスに保存します。updateimage()

しばらく離れる必要がありますが、必要に応じて、戻ってきたときに数時間で例を作成できます。


編集:私は物事を少し再構築し、キャッシュの問題を排除しました。2つの変更を加えるだけです。updateimage()1つ目は、関数を次の関数に置き換えることです。

function updateimage() {    
    var TempImgData = ImgContext.getImageData(0, 0, ImgCanvas.width, ImgCanvas.height);
    var NewData = TempImgData.data;
    var OrigData = ImgData.data;

    //Change image color
    var len = 4*ImgData.width*ImgData.height-1;
    for(var i=0;i<=len;i+=4) {
        NewData[i+0] = OrigData[i+0] * color.r;
        NewData[i+1] = OrigData[i+1] * color.g;
        NewData[i+2] = OrigData[i+2] * color.b;
        NewData[i+3] = OrigData[i+3];
    }

    //Put changed image onto the canvas
    ImgContext.putImageData(TempImgData, 0, 0);
}

draw()2つ目は、最後の行を次のように更新することです。

drawImg(ImgCanvas, Positions[i].x, Positions[i].y, Positions[i].x+Positions[i].y);

この更新されたコードを使用して、元のベース(白)画像データを参照し、関数を実行するたびにそれに基づいて新しい値を計算しupdateimage()ます。電話をかけると、キャンバス上の画像データのコピーgetImageData()を受け取るため、キャンバスまたはデータを編集しても、もう一方は変更されません。そもそも元のベース画像データを取得しているので、更新するたびに再取得するのではなく、それを使用するのが理にかなっています。

また、画像の色をわずかに変更するループを変更したことに気付くでしょう。アクセス/変更する実際のデータ配列へのハンドルを取得することにより、ループを通過するたびに親オブジェクトから配列の場所を解決する必要がなくなります。この手法により、実際にはかなり優れたパフォーマンスの向上が得られます。パフォーマンスは最初は問題ありませんでしたが、非常に単純なので、効率を上げることは問題ありません。

于 2011-10-03T20:37:30.923 に答える