简体   繁体   English

绘制HTML 5画布的最快方法是什么?

[英]What's the fastest way to draw to an HTML 5 canvas?

I'm investigating the possibility of producing a game using only HTML's canvas as the display media. 我正在调查使用HTML画布作为显示媒体制作游戏的可能性。 To take an example task I need to do, I need to construct the game environment from a number of isometric tiles. 为了完成我需要做的示例任务,我需要从多个等距区块构建游戏环境。 Of course, working in 2D means they by necessity come in rectangular packages so there's a large overlap between tiles. 当然,在2D工作意味着他们必须采用矩形包装,因此瓷砖之间有很大的重叠。

I'm old enough that the natural solution to this problem is to call BitBltMasked. 我已经够大了,这个问题的自然解决方案就是调用BitBltMasked。 Oh wait, no, an HTML canvas doesn't have something as simple and as pleasing as BitBlt. 哦等等,不,HTML画布没有像BitBlt那样简单和令人愉悦的东西。 It seems that the only way to dump pixel data in to a canvas is either with drawImage() which has no useful drawing modes that ignore the alpha channel or to use ImageData objects that have the image data in an array.. to which every. 似乎将像素数据转储到画布的唯一方法是使用drawImage(),它没有用于忽略alpha通道的有用绘图模式,或者使用具有数组中图像数据的ImageData对象。 access. 访问。 is. 是。 bounds. 界限。 checked. 检查。 and. 和。 therefore. 因此。 dog. 狗。 slow. 慢。

OK, that's more of a rant than a question (things the W3C like tend to provoke that from me), but what I really want to know is how to draw fast to a canvas? 好吧,这更像是一个咆哮而不是一个问题(W3C喜欢的东西往往会引起我的注意),但我真正想知道的是如何快速绘制画布? I'm finding it very difficult to ditch the feeling that doing 100s of drawImages() a second where every draw respects the alpha channel is inherently sinful and likely to make my application perform like arse in many browsers. 我发现很难放弃做100s drawImages()的感觉,每次抽奖都尊重alpha通道,这本身就是有罪的,并且可能使我的应用程序在许多浏览器中表现得像屁股。 On the other hand, the only way to implement BitBlt proper relies heavily on a browser using a hotspot-like execution technique to make it run fast. 另一方面,实现BitBlt的唯一方法在很大程度上依赖于浏览器使用类似热点的执行技术来使其快速运行。

Is there any way to draw fast across every possible implementation, or do I just have to forget about performance? 有没有办法快速划分每个可能的实现,或者我只是忘记性能?

This is a really interesting problem, and there's a few interesting things you can do to solve it. 这是一个非常有趣的问题,你可以做一些有趣的事情来解决它。

First, you should know that drawImage can accept a Canvas, not just an image. 首先,您应该知道drawImage可以接受Canvas,而不仅仅是图像。 The "sub-Canvas"es don't even need to be in the DOM. “sub-Canvas”甚至不需要在DOM中。 This means that you can do some compositing on one canvas, then draw it to another. 这意味着您可以在一个画布上进行一些合成,然后将其绘制到另一个画布上。 This opens a whole world of optimization opportunities, especially in the context of isometric tiles. 这开启了整个世界的优化机会,特别是在等距瓷砖的背景下。

Let's say you have an area that's 50 tiles long by 50 tiles wide (I'll say meters for the sake of my own sanity). 假设你的区域有50个瓷砖长50个瓷砖宽(为了我自己的理智,我会说米)。 You might divide the area into 10x10m chunks. 您可以将区域划分为10x10m块。 Each chunk is represented by its own Canvas. 每个块都由自己的Canvas表示。 To draw the full scene, you'd simply draw each of the chunks' Canvas objects to the main canvas that's shown to the user. 要绘制完整场景,您只需将每个块的Canvas对象绘制到显示给用户的主画布上。 If only four chunks (a 20x20m area), you would only perform four drawImage operations. 如果只有四个块(一个20x20m的区域),则只执行四次drawImage操作。

Of course, each of those individual chunks will need to render its own Canvas. 当然,每个单独的块都需要渲染自己的Canvas。 On game ticks where nothing happens in the chunk, you simply don't do anything: the Canvas will remain unchanged and will be drawn as you'd expect. 在游戏刻度中,块中没有任何反应,您根本不做任何事情:画布将保持不变并将按照您的预期绘制。 When something does change, you can do one of a few things depending on your game: 当某些事情发生变化时,你可以根据你的游戏做一些事情:

  1. If your tiles extend into the third dimension (ie: you have a Z-axis), you can draw each "layer" of the chunk into its own Canvas and only update the layers that need to be updated. 如果您的切片延伸到第三维(即:您有一个Z轴),您可以将切块的每个“图层”绘制到自己的画布中,并仅更新需要更新的图层。 For example, if each chunk contains ten layers of depth, you'd have ten Canvas objects. 例如,如果每个块包含十层深度,则您将拥有十个Canvas对象。 If something on layer 6 was updated, you would only need to re-paint layer 6's Canvas (probably one drawImage per square meter, which would be 100), then perform one drawImage operation per layer in the chunk (ten) to re-draw the chunk's Canvas. 如果更新了第6层上的某些内容,您只需要重新绘制第6层的Canvas(可能是每平方米一个drawImage ,这将是100),然后在块(10)中每层执行一次drawImage操作以重新绘制大块的画布。 Decreasing or increasing the chunk size may increase or decrease performance depending on the number of update you make to the environment in your game. 减少或增加块大小可能会增加或减少性能,具体取决于您对游戏环境所做的更新次数。 Further optimizations can be made to eliminate drawImage calls for obscured tiles and the like. 可以进一步优化以消除对于模糊的瓦片等的drawImage调用。
  2. If you don't have a third dimension, you can simply perform one drawImage per square meter of a chunk. 如果你没有第三个维度,你可以简单地每平方米一个块执行一个drawImage If two chunks are updated, that's only 200 drawImage calls per tick (plus one call per chunk visible on the screen). 如果更新了两个块,那么每个tick只有200个drawImage调用(加上屏幕上每个块可以调用一个)。 If your game involves very few updates, decreasing the chunk size will decrease the number of calls even further. 如果您的游戏只涉及很少的更新,减少块大小将进一步减少调用次数。
  3. You can perform updates to the chunks in their own game loop. 您可以在自己的游戏循环中对块进行更新。 If you're using requestAnimationFrame (as you should be), you only need to paint the chunk Canvas objects to the screen. 如果你正在使用requestAnimationFrame (你应该这样),你只需要将块Canvas对象绘制到屏幕上。 Independently, you can perform game logic in a setTimeout loop or the like. 独立地,您可以在setTimeout循环等中执行游戏逻辑。 Then, each chunk could be updated in its own tick between frames without affecting performance. 然后,每个块可以在帧之间自己更新,而不会影响性能。 This can also be done in a web worker using getImageData and putImageData to send the rendered chunk back to the main thread whenever it needs to be updated, though making this work seamlessly will take a good deal of effort. 这也可以在web worker中使用getImageDataputImageData在需要更新时将渲染的块发送回主线程,尽管无缝地完成这项工作需要花费很多精力。

The other option that you have is to use a library like pixi.js to render the scene using WebGL. 您拥有的另一个选项是使用像pixi.js这样的库来使用WebGL渲染场景。 Even for 2D, it will increase performance by decreasing the amount of work that the CPU needs to do and shifting that over to the GPU. 即使对于2D,它也会通过减少CPU需要做的工作量并将其转移到GPU来提高性能。 I'd highly recommend checking it out. 我强烈建议您查看它。

I know that GameJS has blit operations, and I certainly assume any other html5 game libraries do as well (gameQuery, LimeJS, etc etc). 我知道GameJS有blit操作,我当然也认为其他任何html5游戏库都有(gameQuery,LimeJS等)。 I don't know if these packages have addressed the specific array-bounds-checking concern that you had, but in practice their samples seem to work plenty fast on all platforms. 我不知道这些软件包是否已解决了您所遇到的特定数组边界检查问题,但实际上它们的示例似乎在所有平台上都能快速运行。

You should not make assumptions about what speedups make sense. 你不应该假设加速是有意义的。 For example, the GameJS developer reports that he was going to implement dirty rectangle tracking but it turned out that modern browsers do this automatically--- link . 例如,GameJS开发人员报告说他将实现脏矩形跟踪,但事实证明现代浏览器会自动执行此操作--- 链接

For this reason and others, I suggest to get something working before thinking about the speed. 出于这个原因和其他原因,我建议在考虑速度之前先做点工作。 Also, make use of drawing libraries, as the authors have presumably spent some time optimizing performance. 此外,使用绘图库,因为作者可能花了一些时间来优化性能。

I have no personal knowledge about this, but you can look into the appMobi "direct canvas" HTML element which is allegedly a much faster version of normal canvas, link . 我对此没有个人知识,但是您可以查看appMobi“直接画布”HTML元素,据称这是一个更快的普通画布链接版本。 I'm confused about whether this works in all browsers or just webkit browsers or just appMobi's own special browser. 我很困惑这是适用于所有浏览器还是仅适用于webkit浏览器,或者仅适用于appMobi自己的特殊浏览器。

Again, you should not make assumptions about what speedups make sense without a very deep knowledge of web browser internal processes. 同样,如果没有对Web浏览器内部流程的深入了解,您不应该假设加速是有意义的。 That webpage about "direct canvas" mentions a bunch of things that slow down canvas-drawing: "Reflowing text, mapping hot spots, creating indexes for reference links, on and on." 关于“直接画布”的网页提到了一系列减慢画布速度的事情:“重新流动文本,映射热点,为参考链接创建索引,不断创建。” Alpha-blending and array-bounds-checking are not mentioned as prominent causes of slowness! Alpha混合和数组边界检查没有被提及作为缓慢的主要原因!

Unfortunately, there's no way around the alpha composition overhead. 不幸的是,没有办法解决alpha组合开销问题。 Clipping may be one solution, but I doubt there would be much, if any, performance gain. 剪切可能是一种解决方案,但我怀疑会有多少(如果有的话)性能提升。 Not to mention how complicated such a route would be to implement on irregular shapes. 更不用说这种路线在不规则形状上的实施有多复杂。

When you have to draw the entire display, you're going to have to deal with the performance hit. 当你必须绘制整个显示器时,你将不得不处理性能损失。 Although afterwards, you have a whole screen's worth of pre-calculated alpha imagery and you can draw this image data at an offset in one drawImage call. 虽然之后,您有一个完整屏幕的预先计算的alpha图像,您可以在一个drawImage调用中以偏移量绘制此图像数据。 Then, you would only have to individually draw the new tiles that are scrolled into view. 然后,您只需要单独绘制滚动到视图中的新切片。

But still, the browser is having to redraw each pixel at a different location in the canvas. 但是,浏览器仍然需要在画布中的不同位置重绘每个像素。 Which is quite expensive. 这是非常昂贵的。 It would be nice if there was a method for just scrolling pixels, but no luck there either. 如果只有滚动像素的方法会很好,但也没有运气。

One idea that comes to mind is that you could implement multiple canvases, translating each individual canvas instead of redrawing the pixels. 想到的一个想法是,您可以实现多个画布,翻译每个单独的画布而不是重绘像素。 This would allow the browser to decide how to redraw those pixels, in a more native way, at least in theory anyway. 这将允许浏览器以更原生的方式决定如何重绘这些像素,至少在理论上是这样。 Then you could render the newly visible tiles on a new, or used/cached, canvas element. 然后,您可以在新的或已使用/缓存的canvas元素上呈现新显示的tile。 Positioning it to match up with the last screen render. 定位它以匹配最后一个屏幕渲染。

But that's just my two blits... I mean bits... duh, I mean cents :] 但那只是我的两个blits ...我的意思是位...呃,我的意思是美分:]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 用HTML5绘制最快的方法是什么 - What is the fastest way to draw in HTML5 解析此HTML表的最快方法是什么? - What's the fastest way to parse this HTML table? HTML5 Canvas-画出越来越粗的线的最简单方法是什么? - HTML5 Canvas - What's the simplest way to draw a line that gets thicker and thicker to the end? 在HTML5Canvas中绘制和填充非抗锯齿圆的最快方法 - Fastest way to draw and fill a NOT anti-aliasing circle in HTML5Canvas 在HTML canvas元素上绘制文本的最佳方法是什么? - What is the best way to draw text on an HTML canvas element? 从 HTML Canvas 捕获图像的最快方法 - Fastest way to capture image from a HTML Canvas 在HTML5 canvas元素中移动矩形(像素)区域的最快方法是什么 - What is the fastest way to move a rectangular (pixel) region inside a HTML5 canvas element 从 RGB 像素数组在 JavaScript 中将图像绘制到屏幕的最快方法是什么? - What's the fastest way to draw an image to the screen in JavaScript from array of RGB pixels? 在 HTML5 画布中创建关键事件的最佳方法是什么? - What's the best way to create key events in HTML5 canvas? 将一个数组缓冲区绘制到画布上的最佳方法是什么? - What is the best way, to draw a part of an arraybuffer to the canvas?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM