在canvas javascript上绘制10,000个对象
draw 10,000 objects on canvas javascript
我需要在画布上绘制超过10,000张图像(32x32像素),但超过2000张图像的表现非常糟糕。
这是一个小例子:
对象结构{position:0}
for(var nObject = 0; nObject < objects.length; nObject++){
ctx.save();
ctx.translate(coords.x,coords.y);
ctx.rotate(objects[nObject].position/100);
ctx.translate(radio,0);
ctx.drawImage(img,0,0);
ctx.restore();
objects[nObject].position++;
}
用这段代码,我平移了一个坐标周围的图像。
你有什么建议来提高性能?
更新:
我尝试分层,但是性能变差了
http://jsfiddle.net/72nCX/3/
我可以给你10,000,但有两个主要缺点。
-
你可能会注意到图像不完全尊重透明度,这是可以修复的。但这超出了这个答案的范围
-
你将不得不使用数学来做任何类型的转换,因为标准的画布转换矩阵不能应用于ImageData
因此,为了获得最快的性能,您需要使用ImageData。这基本上是在每个像素级别上访问canvas元素,并允许你做各种很酷的事情。我使用了两种主要方法。
- putImageData
- createImageData
这里还有一个很好的教程,可以帮助你更好地理解它。
首先我为图片
创建了一个临时画布imgToDraw.onload = function () {
// In memory canvas
imageCanvas = document.createElement("canvas"),
iCtx = imageCanvas.getContext("2d");
// set the canvas to the size of the image
imageCanvas.width = this.width;
imageCanvas.height = this.height;
// draw the image onto the canvas
iCtx.drawImage(this, 0, 0);
// get the ImageData for the image.
imageData = iCtx.getImageData(0, 0, this.width, this.height);
// get the pixel component data from the image Data.
imagePixData = imageData.data;
// store our width and height so we can reference it faster.
imgWidth = this.width;
imgHeight = this.height;
draw();
};
Next是渲染函数
中的主要部分我只是把相关的部分贴出来。
// create new Image data. Doing this everytime gets rid of our
// need to manually clear the canvas since the data is fresh each time
var canvasData = ctx.createImageData(canvas.width, canvas.height),
// get the pixel data
cData = canvasData.data;
// Iterate over the image we stored
for (var w = 0; w < imgWidth; w++) {
for (var h = 0; h < imgHeight; h++) {
// make sure the edges of the image are still inside the canvas
// This also is VERY important for perf reasons
// you never want to draw outside of the canvas bounds with this method
if (entity.x + w < width && entity.x + w > 0 &&
entity.y + h > 0 && entity.y + h < height) {
// get the position pixel from the image canvas
var iData = (h * imgWidth + w) * 4;
// get the position of the data we will write to on our main canvas
// the values must be whole numbers ~~ is just Math.floor basically
var pData = (~~ (entity.x + w) + ~~ (entity.y + h) * width) * 4;
// copy the r/g/b/ and alpha values to our main canvas from
// our image canvas data.
cData[pData] = imagePixData[iData];
cData[pData + 1] = imagePixData[iData + 1];
cData[pData + 2] = imagePixData[iData + 2];
// this is where alpha blending could be applied
if(cData[pData + 3] < 100){
cData[pData + 3] = imagePixData[iData + 3];
}
}
}
}
// now put all of that image data we just wrote onto the actual canvas.
ctx.putImageData(canvasData, 0, 0);
从这里得到的主要结论是,如果你需要在画布上绘制大量的对象,你不能使用drawImage
,像素操作是你的朋友。
标题>
我想这就是你需要的。
Eric Rowell (KineticJS的创造者)在这里做了一些压力测试。他说:
"创建10个图层,每个图层包含1000个形状,创建10,000个形状。这极大地提高了性能,因为当从一个图层中移除一个圆时,一次只需要绘制1000个形状,而不是绘制10000个形状。"
"请记住,层太多也会降低性能。我发现,使用10层每层1000个形状比20层每层500个形状或5层每层2000个形状表现得更好。"
Update:您将需要运行测试用例,其中最优化的过程将适合您。例如:10000个形状可以通过以下方式实现:
10000个形状* 1层
5000个形状* 2层
2500个形状* 4层
哪个适合你,就选那个!这取决于你的代码。
您可以执行以下步骤来提高性能:
- 首先去除
save
/restore
-它们是非常昂贵的调用,可以用setTransform
代替 - 展开循环,在每次迭代中做更多的事情
- 缓存所有属性
小提琴
循环展开4次的例子:
for(var nObject = 0,
len = objects.length, // cache these
x = coords.x,
y = coords.y; nObject < len; nObject++){
ctx.setTransform(1,0,0,1, x, y); // sets absolute transformation
ctx.rotate(objects[nObject].position*0.01);
ctx.translate(radio,0);
ctx.drawImage(imgToDraw,0,0);
objects[nObject++].position++;
ctx.setTransform(1,0,0,1,x, y);
ctx.rotate(objects[nObject].position*0.01);
ctx.translate(radio,0);
ctx.drawImage(imgToDraw,0,0);
objects[nObject++].position++;
ctx.setTransform(1,0,0,1,x, y);
ctx.rotate(objects[nObject].position*0.01);
ctx.translate(radio,0);
ctx.drawImage(imgToDraw,0,0);
objects[nObject++].position++;
ctx.setTransform(1,0,0,1,x, y);
ctx.rotate(objects[nObject].position*0.01);
ctx.translate(radio,0);
ctx.drawImage(imgToDraw,0,0);
objects[nObject++].position++;
}
ctx.setTransform(1,0,0,1,0,0); // reset transform for rAF loop
(但不要期望实时性能)。
虽然,在这么小的区域里画2000个对象可能有点没有意义。如果您追求效果,我建议使用以下方法:
- 创建屏幕外画布
- 用上述方法生成5-8帧并存储为图像
- 按原样播放这5-8张图片,而不是做所有的计算
如果你需要更流畅的外观,只需制作更多的帧。你可以将每个帧存储在基于单元格的单个画布中,然后将其用作精灵表。当绘制时,你当然必须注意当前位置是静态的,而不是实际动画时的移动。旋转和由此产生的位置是另一个因素。
经过各种测试,我得出以下结论:
- 画布没有此任务的容量。
- 分层画布只在静态元素不需要不断重绘时才有助于性能。
- 添加坐标打印限制有助于渲染。
- 不要打印最终会被另一个具有更高z-index的元素隐藏的元素。
最终结果是所有贡献的一个小混合。但需要改进。
测试了30,000个对象,性能保持在60/fps。
http://jsfiddle.net/NGn29/1/ var banPrint = true;
for(nOverlap = nObject; nOverlap < objects.length; nOverlap++){
if(
objects[nOverlap].position == objects[nObject].position
&& nOverlap != nObject
){
banPrint = false;
break;
}
}
如果图像不重叠,则生成的图像是3200x3200像素,这比大多数显示器所能显示的要大。所以你可以尝试得到转换后的图像的边界框,跳过那些在可见区域之外的(即使画布已经为你做了)。
另一个想法是将小图像组合成大图像,并将它们组合成一个组。
如果你想把图像组织成一个环,那么你可以把它们画一次,作为一个环,保存为一个图像,然后旋转"环图像",而不是每个单独的图像。
最后,看看WebGL,它可能比2D canvas
API更有效。
- 分析Javascript函数中的多个对象
- 正在更新mongod中两个对象内部的数组
- 如何循环通过2个对象数组并通过匹配id进行合并
- 添加两个对象以获取值的总和
- 如何使用javascript合并两个对象数组
- 正在加载具有相同功能的多个对象
- 在JavaScript中相等两个对象.在更改一时,秒会自动更改
- 在不使用循环的情况下,从一个数据库字符串值向javascript数组添加多个对象
- 如何使用AngularJs比较两个对象的JSON
- 如何在javascript中合并两个对象数组
- JavaScript-将多个对象存储在数组中,并通过访问它们的属性
- JavaScript - 合并两个对象数组并根据属性值删除重复数据
- 我想使用Javascript中的循环在HTML中的特定id中显示数组中的多个对象
- 如何从带有多个对象的JSON字符串创建JSON对象
- Javascript如何确定两个对象是否相同
- 如何将类似类型的多个对象添加到本地存储中
- 比较两个对象数组
- 计算数组中多个对象的属性
- 将超过50,000个字符的JSON字符串解析为javascript对象
- 在canvas javascript上绘制10,000个对象