我试图通过将像素直接绘制到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上的空气) –