I'm reading through the MDN documentation for canvas and above the transformations section, it says "The methods listed below remain for historical and compatibility reasons as DOMMatrix objects are used in most parts of the API nowadays and will be used in the future instead." This seems to suggest that transform methods (such as .rotate()
and .scale()
) used directly aginst the CanvasRenderingContext2D
are obsolete. However, I don't see any clear explanation as to what the new mechanism is for doing things like rotating and scaling the entire canvas using the DOMMatrix mechanism. How can this be done and is there any decent documentation for it? Even MDN's own canvas tutorial still calls transform methods against the canvas rendering context!
These methods aren't obsolete, you are still safe to use them and this paragraph is I believe misleading. I'll think on it but I may end up removing it from MDN since we've got no intention of removing these methods.
And while this will be implementation dependent, I know that at least in Chromium both don't end up in the same path internally, and I wouldn't be surprised that using a DOMMatrix
object would be somehow slower than using the relative transforms. There are also cases where using a DOMMatrix
object just makes your code more complex to read and maintain.
So you'd better not drop the tranform methods just because someone wrote that line in this article.
Anyway, DOMMatrix
objects are convenient and there are definitely cases where you'll want them. To do so, apply the transforms on that object, and then apply it through context.setTransform(matrix)
. This will set the context's current transform matrix (CTM) to the one represented by the DOMMatrix
object, and disregard whatever was set as CTM before.
So for instance to translate your context:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 50, 50); // untransformed
const mat = new DOMMatrix();
// mat.translate() would return a new DOMMatrix without modifying this one
mat.translateSelf(120, 50);
// set the context CTM to our DOMMatrix
ctx.setTransform(mat);
ctx.fillStyle = "green";
ctx.fillRect(0, 0, 50, 50); // transformed
<canvas></canvas>
However beware there is a huge bug in the DOMMatrix API: the rotation angle has been wrongfully defined as degrees. This is basically the only place in almost all the Web-APIs that a JS angle is defined as degrees instead of being defined as radians. So we have to do stoopid conversions there and scratch our head every time we see our rotation didn't work...
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const angle = Math.PI * 1.8; // radians
ctx.translate(150, 75);
ctx.rotate(angle);
ctx.translate(-50, -50);
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, 50, 50); // default ctx.rotate();
const mat = new DOMMatrix();
mat.translateSelf(150, 75);
mat.rotateSelf(angle); // this should have been in degrees!
mat.translateSelf(-50, -50);
ctx.setTransform(mat);
ctx.fillStyle = "green";
ctx.fillRect(0, 0, 50, 50); // that's not what we expected
<canvas></canvas>
Also, to make only relative updates to the current transform matrix (CTM), you'd have to either keep your DOMMatrix
object around in your code, or to retrieve it from the context's .getTransform()
method.
Once you got the context's CTM, you can either apply relative transforms using the DOMMatrix.\[...\]Self
methods, or even multiply this DOMMatrix
object with another one.
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.fillStyle = "green";
ctx.translate(150, 75); // not gonna disappear
const identity = new DOMMatrix();
const anim = () => {
const mat = ctx.getTransform();
ctx.setTransform(identity); // to clear the context, reset to identity
// after you got the previous CTM
ctx.clearRect(0, 0, canvas.width, canvas.height);
mat.rotateSelf(1); // one degree
ctx.setTransform(mat);
ctx.fillRect(-25, -25, 50, 50);
requestAnimationFrame(anim);
};
requestAnimationFrame(anim);
<canvas></canvas>
Finally, note that while the DOMMatrix
interface does support 3D transforms, the canvas 2D API still doesn't support non-affine transformations. You still won't have perspective even when passing a 3D transform.