We've probably all made a mouse-driven selection rect. But what about when the layer has a transform applied - maybe it was dragged, or perhaps scaled at a point via the mouse wheel. But now the rect is all wrong. How to fix that...
This follows on from this earlier post about finding the center of the current view in which we get point matching the center of the canvas on a scaled + rotated + translated layer.
TLDR: This is another case where we have to employ the process of inversing the layer's absolute transform. Absolute because the stage (the layers parent) can also be transformed. After that its relatively straightforward. See demo below.
The mission
What's the issue with drawing a rect on the layer? Surely that's about the most simple thing you can do. Well, not really. If the stage and layer have not been dragged, rotated or scaled, then sure, life is easy. Why would I do that, you ask? Any dragging or zooming will affect the position of the stage or layer - depending on how you scale. An rotation obviously moves us away from the horizontal and vertical axes.
Which tells us that the mission is to make a fool proof function to use the mouse to draw what looks like an un-rotated rect on a worst case rotated, translated and scaled layer setup.
The solution
2D vector graphics engines - like Konva and the HTML5 canvas - use affine transformations. There's a great video explainer of affine transformations here by Steve Seitz.
So when a layer is transformed, if we draw a rect on it, then that will also appear transformed. This is because the child shapes of a container live in a world that they perceive as untransformed, whilst in fact the universe around them has the transform applied.
Therefore, just taking rotation as an example, our understanding should be that while we draw what appears to be an unrotated rect on the canvas surface, at the rotated layer this IS a rotated rect. And in fact it is easier to draw a Konva.Line than try to calculate a Konva.Rect.
And because a Konva.Line is made from drawing a straight line between a list of points, we have to find the (x, y) points of the corners of the rectangle on the layer.
Here's the magic function to take a point from Stage.getPointerPosition() and get the matching point on the layer.
// Get the pointer at this absolute point on the rotated /scaled / translated layer.
function getLayerPt(pt) {
const
// Get the layer's transform
transform = layer.getAbsoluteTransform().copy(),
// Invert the transform to go from viewport space to layer space
inverseTransform = transform.invert(),
// Convert point to layer space
c1 = inverseTransform.point({ x: pt.x, y: pt.y });
return c1
}The process is straightforward - layer.getAbsoluteTransform() gets the absolute transform (meaning including the affect of parent transforms). Note the copy() process - we have to get a copy and not work on the original otherwise it will affect the layer! We then invert the transform and employ the Konva.Transform.point method to process the absolute point returned from Stage.getPointerPosition() to its position on the layer.
From here it's a job of repeating that function call for the 4 corners of the absolute rect then feeding the generated points to a Konva.Line that is added to the layer.
Job done.
Summary
We've seen another example of how getting the inverse transform can make easy work of achieving what would otherwise complicated and fragile DIY math. And we have a takeaway function and demo to illustrate how it works.
Thanks for reading.
VW Aril 2025