firefoxとchromeのcanvas実験
おひさしぶりです。いきてます。そらんです。
最近HTML5のcanvasをいじってます。そこで気付いたことがあったので書きます。というか、ネタはすごい勢いで増えてるんですが、エントリを書く暇がないw
本題なのですが、canvasに画像を30fpsで描画するコードを書きました。
それを書いていてわかったことは、
・ChromeはdrawImageを実行するたびにRAMを確保する。
・FirefoxはsrcにURLをセットしてダウンロードさせているときにRAMを確保する
という挙動をすることです。どちらも本来のサイズよりかなり大きいです。そして30fpsで動かすと、これらがガベージコレクションが追い付かないまま進んでしまうので、すぐメモリを使いきってChromeならcanvasをロックして一切描画できなくなってしまったり、Firefoxならダウンロードが止まったり、ブラウザそのものが落ちたりします。
ここで、Firefoxは、500枚ダウンロードして、20秒待って…というコードをかいたところガベージコレクションが追い付くようになり、目的の挙動をするようになりました。
ただ、Chromeのほうはcanvasそのものへの制約なので、連続的なアニメーションを避ける以上の回避策は今の実装ではなさそうです。
ちなみにFirefoxはdrawImage中、とても安定して動作していました。
以下に、Firefoxでまともに動くCodeを。Chromeだと、単純ダウンロードの場合でもこの場合でも同じようにRAMを使いきった段階でcanvasがロックされてしまいます。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Hello, Woeld!</title> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script> <script type="text/javascript"> <!-- $(document).ready(function(){ init(); }); //----- //Initializing //----- function init(){ canvas = document.getElementById('main'); if ( ! canvas || ! canvas.getContext ) { return false; } ctx = canvas.getContext('2d'); ctx.fillStyle = 'rgb(255, 255, 255)'; loaded = 0; for(var i=0; i<1000; i++){ layers[i] = new Layer(); layers[i].setImage("movie/" + ("0000"+(i + 501)).slice(-5) + ".jpg",0,0,640,360,1); layers[i].img.onload = function(){ loaded++; $("p").html("loaded image:"+loaded); if(loaded == 1000){ starttimer(); $("p").html("loaded image:"+loaded+"nya"); starttimer2(); } } } } firsttimer = 0; function starttimer(){ if(firsttimer == 0){ var timer; timer = setTimeout("starttimer()", 20000); firsttimer = 1; } else { for(var i=loaded; i<loaded + 500; i++){ layers[i] = new Layer(); layers[i].setImage("movie/" + ("0000"+(i + 501)).slice(-5) + ".jpg",0,0,640,360,1); layers[i].img.onload = function(){ loaded++; $("p").html("loaded image:"+loaded); if(loaded%500 == 0){ $("p").html("loaded image:"+loaded+"nya"); if(loaded == 9000){ } else { timer = setTimeout("starttimer()", 20000); firsttimer++; } } } } } } function starttimer2() { setInterval("onTimerEvent()", 1000/30); } var t2 = -1; //var t2 = 2000; function onTimerEvent(){ if(t2>loaded){ t2=0; } t2++; //----- //Animation test with hatune miku! //----- var top = t2 + 1; $("div").html(t2); //----- //Drawing layers //----- ctx.globalAlpha = layers[top].alpha; ctx.drawImage( layers[top].img, layers[top].x, layers[top].y, layers[top].width, layers[top].height ); } //----- //Layer class //----- var Layer = function(){ this.img = new Image(); this.setImage = function(src, x, y, w, h, a){ this.img.src = src; this.x = x; this.y = y; this.width = w; this.height = h; this.alpha = a; this.active = 1; } this.setPos = function(x, y){ this.x = x; this.y = y; } } var layers = new Array(9000); //--> </script> </head> <body> <h1>Canvas</h1> <canvas id="main" width="640" height="480"></canvas> <p></p> <div></div> </body> </html>
改築を繰り返したコードなのでいろいろ無駄はありますが、ひとまず9000枚の画像をロードして表示するプログラムです。
ちなみに実験時は、こちらの動画をお借りしました。
http://www.nicovideo.jp/watch/sm11299633