我試圖通過將像素直接繪製到imageData緩衝區來更新JavaScript畫布。基本上,我在每次mousemove/touchmove事件後更新imageData緩衝區上的所有像素,並嘗試獲得最佳性能。Javascript畫布緩衝區/ iOs Safari和Chrome上的緩慢性能
背景: 我正在開發一個基於emscripten的應用程序,其中畫布上的圖形完全由「本地」代碼逐個像素繪製。我在這個問題中給出的例子是一個更簡單的例子,我轉載了我的問題。
我現在有encoutered兩個性能問題:
- iOS上的Safari瀏覽器(在iPad上空氣測試):繪圖函數被調用在31 fps的,但屏幕上的畫布渲染laggy(視覺,我會說這是在10fps的最大更新,再加上0.5秒它不更新的話)
- iOS上的Chrome瀏覽器的一些間隔:性能是可怕的,因爲我得到2.9 fps的
在一個桌面mac,我得到一個穩定的表現:55fps與firefo鉻x和45 fps的
所以,我有兩個問題
- 如何將迫使畫布被刷新iOS上的Safari瀏覽器(更快,纔能有一個真正的每秒30幀的渲染,或者可能是一個小更低)?
- 如何優化性能?我錯過了可能的優化嗎?
請參考下面的代碼:它是一個單獨的html文件,它再現了我的問題。
我知道我可以使用webworker,但由於我使用emscripten這不會是最佳的(每個webworker開始一個新的內存,我需要保持記錄的狀態)。
請參閱此處的代碼(這是一個單獨的html文件,js是自包含的)。請在畫布內移動鼠標以查看計算出的fps。
<canvas width=800 height=600 id="canvas"> </canvas>
<script>
//Disable scroll : usefull for tablets where touch events
//will scroll the page
function DisableScroll()
{
window.addEventListener("touchmove", function(event) {
if (!event.target.classList.contains('scrollable')) {
// no more scrolling
event.preventDefault();
}
}, false);
}
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000/60);
};
})();
window.countFPS = (function()
{
var nbSamples = 20; //number of samples before giving a fps
var counter = 0;
var fps = 0;
var timeStart = new Date().getTime();
return function()
{
counter++;
if (counter == nbSamples)
{
var timeEnd = new Date().getTime();
var delaySeconds = (timeEnd - timeStart)/1000;
fps = 1/delaySeconds * nbSamples;
counter = 0;
timeStart = timeEnd;
}
return fps.toFixed(2);
}
}());
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
function getTouchPos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.targetTouches[0].clientX - rect.left,
y: evt.targetTouches[0].clientY - rect.top
};
}
DisableScroll();
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
var canvasData = "empty";
function myDraw(pos)
{
canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var binaryData = canvasData.data;
var idx = 0;
for (y = 0; y < canvas.height; y++)
{
for (x = 0; x < canvas.width; x++)
{
//Red
binaryData[idx ++] = x % 255;
//Green : add a little animation on the green channel
//var dist = Math.sqrt((pos.x - x) * (pos.x - x) + (pos.y - y) * (pos.y - y));
var dist = Math.abs(pos.x - x) + Math.abs(pos.y - y);
var g = 255 - dist;
if (g < 0)
g = 0;
binaryData[idx++] = g;
//Blue
binaryData[idx ++] = y % 255;
//Alpha
binaryData[idx ++] = 255;
}
}
ctx.putImageData(canvasData, 0, 0);
}
var OnLoad = function()
{
myDraw({x:0, y:0});
}
//
// Mouse & touch callbacks
//
function CanvasMouseMove(pos)
{
myDraw(pos);
var elem = document.getElementById("fps");
elem.value = window.countFPS();
}
canvas.addEventListener("touchmove", function(e){ CanvasMouseMove(getTouchPos(canvas, e)); } , false);
canvas.addEventListener("mousemove", function(e){ CanvasMouseMove(getMousePos(canvas, e)); });
</script>
<body onload=OnLoad()>
<br/>
FPS<input type=text id="fps" />
</body>
不要忘記在myDraw中聲明x,y作爲變量。您可以緩存canvas.width和canvas.height以避免DOM訪問,緩存pos.x和pos.y,併爲(&0xFF)交易(%255),並緩存Math.abs(對此不太確定)。所有這些都不會對我擔心的幀速率做出巨大改變。 – GameAlchemist
此外,更重要的是:只需創建一個您不斷修改的imageData。並通過使用一個標誌來繪製requestAnimationFrame。不要使用輸入,而要使用fillText。所有這些fps在幾個瀏覽器中都能提高2到5個。 http://jsbin.com/saruzoqo/2/ – GameAlchemist
不錯,你的改變使得fps在safari/iOS上從30上升到60 fps,而在chrome/iOs上從5上升到10 fps(這是iPad上的空氣) –