如何一次将多个矩形绘制到画布中?

时间:2022-10-19 20:38:10

I'm drawing a large number (~20000) of rectangles to a HTML5 canvas using drawRect, each at a different, predetermined position. I'm doing this from a loop:

我使用drawRect将大量(~20000)矩形绘制到HTML5画布上,每个都位于不同的预定位置。我是从循环中做到的:

for (var i = 0; i < 100; ++i) {
    for (var j = 0; j < 200; ++j) {
        context.fillStyle = '#000000';
        context.fillRect(i * 8, j * 2, 6, 1);
    }
}

Working demo on JSFiddle

关于JSFiddle的工作演示

This snippet draws 100 bars into a 800px wide canvas, each of them 6px wide, and each of them consisting of numerous small (1x6) rectangles stacked below each other: a static count of 200 of them in the provided example, but a dynamically changing count in my application (hence the need for rerendering them).

这个片段将100个条形绘制成800px宽的画布,每个画布宽6px,每个画布都包含许多彼此堆叠的小(1x6)矩形:在提供的示例中静态计数为200,但动态变化在我的应用程序中计数(因此需要重新渲染它们)。

This takes a few tens of milliseconds, nothing serious by itself. But this entire process is invoked repeatedly, which affects performance significantly, and not in the good way.

这需要几十毫秒,本身并不严重。但是重复调用整个过程,这会显着影响性能,而不是以良好的方式。

Is there a solution or workaround to make, say, such a bar be drawn in one canvas instruction, hopefully exploiting hardware acceleration much better?

是否有一个解决方案或解决方法,比如说,在一个画布指令中绘制这样的条形图,希望更好地利用硬件加速?

The rectangles each receive a glow in my app, so slicing through them horizontally is not a good approach. I already tried using an offscreen canvas, drawing there, and rendering it's image output to the main canvas, but there's no noticable performance gain.

每个矩形在我的应用程序中都会发出光晕,因此水平切片并不是一个好方法。我已经尝试使用屏幕外画布,在那里绘图,并将其图像输出渲染到主画布,但没有明显的性能提升。

3 个解决方案

#1


2  

Make yourself a single (offscreen) canvas image containing a single full-height bar, and then for each dynamic height bar only copy the required number of vertical pixels from that onto the onscreen canvas.

使自己成为包含单个全高度条的单个(屏幕外)画布图像,然后对于每个动态高度条仅将所需数量的垂直像素复制到屏幕画布上。

#2


2  

You can initialize useful variables like this:

您可以像这样初始化有用的变量:

var context = canvas.getContext("2d");
var map = context.getImageData(startX, startY, width, height);

where canvas is the return value for something like document.getElementById("myCanvas").

其中canvas是document.getElementById(“myCanvas”)之类的返回值。

startX is the start x, startY is the start y. You can use 0 as values for them, but I prefer to not assume that this starts at the left-top point of the canvas. width is the width and height is the height. Now, you can have your loop and set values, like this:

startX是起始点x,startY是起始点y。您可以使用0作为它们的值,但我不想假设它从画布的左上角开始。 width是宽度,height是高度。现在,您可以拥有循环和设置值,如下所示:

map.data[4 * index] = r; //red
map.data[4 * index + 1] = g; //green
map.data[4 * index + 2] = b; // blue

when you are done with your loop, save things, like this:

当你完成你的循环,保存的东西,像这样:

context.putImageData(map, startX, startY);

The speed should be improved, since you are reading only once and drawing only once. As about the loops, you are just setting values, which is a cheap operation compared to drawing into the canvas. So, the idea of this optimization is, to: read things at once, set the values in a loop and draw only once instead of drawing each time you get some input. You can also omit reading the relevant parts and generate the data yourself, but I decided to show you how you can read data if you need to know something about the already drawn things.

速度应该提高,因为您只阅读一次并且只绘制一次。关于循环,您只需设置值,与绘制到画布相比,这是一个便宜的操作。因此,这种优化的想法是:一次读取内容,在循环中设置值,只绘制一次,而不是每次获得一些输入时绘制。您也可以省略阅读相关部分并自己生成数据,但是如果您需要了解已经绘制的内容,我决定向您展示如何读取数据。

#3


0  

Seeing that this looks some kind of background over which you are going to draw something else, have you considered converting this into a static image and setting the canvas background to this? You could turn off / on the background or whatever you draw on the canvas is going to overlap the background in the areas where the fill / draw happens.

看到这看起来某种背景,你将要绘制其他东西,你是否考虑将其转换为静态图像并将画布背景设置为此?您可以关闭/打开背景,或者在画布上绘制的任何内容将与填充/绘制发生的区域中的背景重叠。

Of, if you are going to adjust this dynamically, a separate canvas element positioned exactly under another one would also be something to consider.

当然,如果你要动态调整它,一个单独位于另一个下面的独立画布元素也需要考虑。

#1


2  

Make yourself a single (offscreen) canvas image containing a single full-height bar, and then for each dynamic height bar only copy the required number of vertical pixels from that onto the onscreen canvas.

使自己成为包含单个全高度条的单个(屏幕外)画布图像,然后对于每个动态高度条仅将所需数量的垂直像素复制到屏幕画布上。

#2


2  

You can initialize useful variables like this:

您可以像这样初始化有用的变量:

var context = canvas.getContext("2d");
var map = context.getImageData(startX, startY, width, height);

where canvas is the return value for something like document.getElementById("myCanvas").

其中canvas是document.getElementById(“myCanvas”)之类的返回值。

startX is the start x, startY is the start y. You can use 0 as values for them, but I prefer to not assume that this starts at the left-top point of the canvas. width is the width and height is the height. Now, you can have your loop and set values, like this:

startX是起始点x,startY是起始点y。您可以使用0作为它们的值,但我不想假设它从画布的左上角开始。 width是宽度,height是高度。现在,您可以拥有循环和设置值,如下所示:

map.data[4 * index] = r; //red
map.data[4 * index + 1] = g; //green
map.data[4 * index + 2] = b; // blue

when you are done with your loop, save things, like this:

当你完成你的循环,保存的东西,像这样:

context.putImageData(map, startX, startY);

The speed should be improved, since you are reading only once and drawing only once. As about the loops, you are just setting values, which is a cheap operation compared to drawing into the canvas. So, the idea of this optimization is, to: read things at once, set the values in a loop and draw only once instead of drawing each time you get some input. You can also omit reading the relevant parts and generate the data yourself, but I decided to show you how you can read data if you need to know something about the already drawn things.

速度应该提高,因为您只阅读一次并且只绘制一次。关于循环,您只需设置值,与绘制到画布相比,这是一个便宜的操作。因此,这种优化的想法是:一次读取内容,在循环中设置值,只绘制一次,而不是每次获得一些输入时绘制。您也可以省略阅读相关部分并自己生成数据,但是如果您需要了解已经绘制的内容,我决定向您展示如何读取数据。

#3


0  

Seeing that this looks some kind of background over which you are going to draw something else, have you considered converting this into a static image and setting the canvas background to this? You could turn off / on the background or whatever you draw on the canvas is going to overlap the background in the areas where the fill / draw happens.

看到这看起来某种背景,你将要绘制其他东西,你是否考虑将其转换为静态图像并将画布背景设置为此?您可以关闭/打开背景,或者在画布上绘制的任何内容将与填充/绘制发生的区域中的背景重叠。

Of, if you are going to adjust this dynamically, a separate canvas element positioned exactly under another one would also be something to consider.

当然,如果你要动态调整它,一个单独位于另一个下面的独立画布元素也需要考虑。