In an earlier post I covered how to rotate a shape around any point on the canvas without using offset. In this post I’ll look at how to rotate and scale a rectangle so that it entirely covers the space it occupied before rotation. This is useful if you ever have to rotate an image without leaving gappy corners.
TLDR: Hop over to view the codepen.
The canvas is built with a 2 rectangles – r1 (blue) & r2 (pink). Rect r2 is rotated from the centre point of the rectangles and scaled so that it always covers all the area of r1.
The function that does the work is shown below. This uses the rotateAroundPoint() function which explained in full in the earlier post. The key technique here is to clear any rotation and scaling of r2, then apply the new rotation and call the r2.getClientRect() method to get the space required to fit the rotated rect. Note no scaling is applied at this stage!
This gives the bounding rect for the unscaled rect rotated to the new angle. We then compute the ratio of width & heights of this bounding rect to the unrotated rect. Finally we take the maximum of those ratios as the new scale to apply to the rect. After that it is just a matter of resetting the rect’s rotation to zero, applying the scale and moving the rect origin to cater for the scaling, and calling the generic rotation function.
function doRotate(angle){
// Increment the angle
totalAngle = totalAngle + angle;
// to get the bounding rect, set the rotation to the new angle andscale = 1
r2.setAttrs({rotation: totalAngle, scaleX: 1, scaleY: 1})
// get the bounding rect of the rotated rectangle.
let br = r2.getClientRect();
// Find the x & y scale and select the largest for use as the scale
let scaleX = br.width / r1.width(),
scaleY = br.height / r1.height(),
scale = Math.max(scaleX, scaleY);
// Apply the scale and position attrs.
r2.setAttrs({
rotation: 0,
scaleX: scale,
scaleY: scale,
position: ({
x: ctrPt.x - scale * r2.width()/2,
y: ctrPt.y - scale * r2.height()/2
})
})
// Apply the rotation about the center point
rotateAroundPoint(r2, totalAngle, ctrPt)
// Move the top corner marker for illustration
c2.position(r2.position())
}
Summary
We now have the effect we wanted. I’m rotating one rect over another so that we can see what goes on outside the target rect area. The original use-case that got me started on this blog came from the Konva Discord group where someone needed to achieve this kind of effect but with an image over the entire stage. To achieve that would be quite straightforward based on this demo.
Thanks for reading.
VW Feb 2022