[英]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++;
}
使用这段代码我可以对坐标周围的图像进行扫描。
您建议什么来提高性能?
更新:
我尝试分层,但表现恶化
我可以给你10,000,但有两个主要的缺点。
您可能会注意到图像完全不尊重透明度,可以修复..但这超出了本答案的范围。
您将不得不使用数学进行任何类型的转换,因为标准画布转换矩阵无法应用于ImageData
因此,要使用canvas和大量对象获得最快的性能,您需要使用ImageData 。 这基本上是在每像素级别访问canvas元素,并允许你做各种很酷的东西。 我使用了两种主要方法。
这里还有一个很好的教程 ,有助于更好地理解它。
所以我做的是首先我为图像创建了一个临时画布
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个形状。这极大地提高了性能,因为在从图层中移除圆圈而不是所有10,000个形状时,只需要绘制1,000个形状。”
“请记住,有太多的层也会降低性能。我发现使用10个层,每个层由1,000个形状组成,表现优于20层,500个形状或5个层,2,000个形状。”
更新:您需要运行测试用例,其中最优化的过程适合您。 示例:可以通过以下任一方式实现10000个形状:
10000个形状* 1层
5000个形状* 2层
2500个形状* 4层
无论哪个适合您,请选择它! 这取决于你的代码。
如果图像不重叠,则生成的图像为3200x3200像素,这比大多数显示器可显示的要多。 因此,您可以尝试获取已转换图像的边界框并跳过可见区域之外的那些(即使画布应该已经为您执行此操作)。
另一个想法是将小图像组合成更大的图像并将它们组合在一起。
如果要将图像组织成环形,则可以将它们作为环形绘制一次,将其另存为图像,然后旋转“环形图像”而不是每个单独的图像。
最后,看看WebGL可能比2D canvas
API更有效。
以下是一些可以提高性能的步骤:
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个物体可能有点毫无意义。 如果你是在效果之后,我建议采用这种方法:
如果你需要更多流体外观,只需生产更多的框架。 您可以根据稍后用作精灵表格的单元格将每个帧存储在单个画布中。 在绘制时,当然必须注意当前位置是静态的而不是实际动画时的移动。 旋转和产生的位置是另一个因素。
经过各种测试,我得出以下结论:
最终结果是所有贡献的小混合。 但需要改进。
测试了30,000个对象,性能保持在60 / fps。
var banPrint = true;
for(nOverlap = nObject; nOverlap < objects.length; nOverlap++){
if(
objects[nOverlap].position == objects[nObject].position
&& nOverlap != nObject
){
banPrint = false;
break;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.