我试图通过直接在pixelData缓冲区上绘制像素来更新javascript画布。基本上我会在每次mousemove / touchmove事件后更新imageData缓冲区上的所有像素,并尝试获得最佳性能。
背景: 我正在开发一个基于emscripten的应用程序,其中画布上的绘图是由" native"逐个像素完全绘制的。码。我在这个问题中给出的例子是一个更简单的例子,我再现了我的问题。
我目前遇到了两个性能问题:
在桌面Mac上,我获得了稳定的性能:使用firefox的55 fps和使用chrome的45 fps
所以,我有两个问题
请参考下面的代码:它是一个单一的html文件,可以重现我的问题。
我知道我可以使用网络工作者,但由于我使用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>
答案 0 :(得分:8)
Rq:
- 避免泄漏全局并在myDraw中声明x,y为vars
意见建议:
- 缓存canvas.width和canvas.height以避免DOM访问,
- 缓存pos.x和pos.y
- (&amp; 0xFF)的交易(%255)
- 缓存Math.abs
- 只需创建一个您继续修改的imageData(释放g.c.)
- 在requestAnimationFrame上绘制(否则你可能需要等待一个框架绘制)
- 缓存画布的边界矩形(及其顶部/左侧值)。
jsbin在这里:
你可以用2个按钮切换旧/新。
看起来像
var staticCanvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
function myDraw2(pos) {
canvasData = staticCanvasData;
var binaryData = canvasData.data;
var cw = canvas.width,
ch = canvas.height;
var posX = pos.x,
posY = pos.y;
var idx = 0;
var abs = Math.abs;
for (var y = 0; y < ch; y++) {
var yDiff = abs(posY - y) ;
for (var x = 0; x < cw; x++) {
//Red
binaryData[idx++] = x & 0xFF;
//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 = abs(posX - x) + yDiff;
var g = 255 - dist;
// if (g < 0) g = 0; // useless array is clamped
binaryData[idx++] = g;
//Blue
binaryData[idx++] = y & 0xFF;
//Alpha
binaryData[idx++] = 255;
}
}
ctx.putImageData(canvasData, 0, 0);
}
结果相当不错,FF需要一半时间(10 vs 20ms)时间,Chrome需要15 ms(116(!)到100),而safari需要7次而不是20次! (mac OS)
我没有进行过多次调查,但事实上似乎不能在每个重绘帐户上创建/复制imageData超过60%的收益。