javascriptperformancehtml5-canvas

HTML5 canvas transform vs manual offsets?


One thing that is often said about canvas performance is that changes to the context's state (like translates, scales, rotates, etc...) are expensive and should be kept to a minimum (e.g. through batching draw commands that use the same transform together).

So my question is, is it better to use manual offsets over transforms when you don't have that many commands that benefit from the transform and you can't really batch them? Or is doing a proper transform just always better?

For example, if I'm drawing little graphics consisting of maybe 1-5 polygons per graphic, and each graphic needs a different transform (e.g. different placement and rotation), it seems inefficient to do a full transform for each of them when I could just calculate the proper positions with a bit of trigonometry.


Solution

  • markE's answer is quite good, but here's what I ultimately ended up settling on for myself:

    While - as pointed out by K3N in a comment - all draw operations go through the transform matrix, that's not actually the issue. Canvas state changes being (relatively) expensive is - that of course includes setTransform. Making a setTransform call for every little thing is inefficient especially if it isn't saving you any calculations (you still have to do trigonometry calculations in order to pass them to setTransform). Performance-wise transforms only provide an benefit if you're doing a lot of drawing with the same transform. Keep in mind that computers are very good at maths.

    With that said, the performance difference is small enough that in the end it's best to go with what is easiest to work with as a programmer/provides the best abstraction. For example one might have some graphics in the form of functions that draw relative to the canvas origin point, so doing setTransform before each of them would allow positioning the graphics without the functions themselves needing to include logic for rotation/positioning/etc. i.e. using transforms would help encapsulation.

    I'd also like to highlight Blindman67's comment on how to efficiently do translate, rotate, and scale in a single setTransform call:

    I have found that the quickest way to set transform translate x,y,rotate r,uniform scale is as follows xx=Math.cos(r)*scale;xy=Math.sin(r)*scale;ctx.setTransform(x‌​x,xy,-xy,xx,x,y); The two trig functions may look slow but they are quicker than a ctx.rotate call. Use it for all render calls and you don't need to restore.