I am wondering how you can rotate an image by only using the transform function. From my understanding this is not possible, since the only things you can do with transform are the following:
Source: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Transformations
And I don't see how any of these would be able to rotate the shape, is this even possible. I assume it must be possible, since rotate is in-fact a type transformation.
The transform is a set of 6 numbers. The 6 numbers as 3 pairs represent the direction and scale of the x axis, the direction and scale of the y axis, and the position of the origin.
The default transform (called the identity matrix) has the values ctx.setTransform(1, 0, 0, 1, 0, 0)
meaning that
{x: 1, y: 0}
left to right {x: 0, y: 1}
top to bottom If we scale the transform we increase the length of the first two vectors. To scale by 2 the transform is ctx.setTransform(2, 0, 0, 2, 0, 0);
{x: 2, y: 0}
left to right {x: 0, y: 2}
top to bottom If we want to rotate by 90deg a square 256 by 256 image then the transform is ctx.setTransform(0, 1, -1, 0, 256, 0)
{x: 0, y: 1}
{x: -1, y: 0}
right to left Thus if we run
ctx.setTransform(0, 1, -1, 0, 256, 0);
ctx.drawImage(myImage, 0, 0, 256, 256); // will draw image rotated 90deg CW
We get a rotated image.
A vector is two values that have ax and y value. The vector defines a direction and length.
To convert a direction to a vector we use sin and cos
const myDirection = angle;
const myDirectionAsRadians = angle * (Math.PI / 180); // convert angle to radians
const x = Math.cos(myDirectionAsRadians)
const y = Math.sin(myDirectionAsRadians)
If we set myDirection
to 90 (deg) then x = 0
and y = 1
pointing down the canvas
Using sin and cos creates a vector in any direction. It has a special property in that its length is always 1. We call such a vector a Unit vector. You may sometimes see a vector being normalized. This converts a vector of any length to a unit vector. It is done by dividing the vector x and y by its length.
function normalize(vector) {
const length = Math.hypot(vector.x, vector.y);
vector.x /= length;
vector.y /= length;
}
NOTE a vector with zero length eg x: 0, y:0
can not be normalized. Not because it has no length (the length is 0) but because it has no direction.
We can define an angle and a scale
const myDirection = -90;
const myDirectionAsRadians = -90 * (Math.PI / 180); // -90 as radians
const myScale = 2;
const x = Math.cos(myDirectionAsRadians) * myScale
const y = Math.sin(myDirectionAsRadians) * myScale
Now for -90 deg the vector is x = 0
and y = -2
pointing up and two CSS pixels long.
For a uniform scale and rotation (the image is always square) all we need is a single vector. For example from the above. x = 0
and y = -2
(pointing up) can be rotated 90 CW by swapping the two components and negating the new x. eg xx = -y
and y = x
to get xx = 2
and y = 0
2 CSS pixels from left two right. Thus we have the direction and scale of both the x and y axis. With the y axis always 90 CW from the x.
To create a transform that rotates any angle and scales by any amount
function scaleAndRotate(scale, rotate) { // rotate is in radians
// get direction and length of x axis
const xAX = Math.cos(rotate) * scale;
const xAY = Math.sin(rotate) * scale;
// get direction and length of y axis that is 90 deg CW of x axis and same length
const [yAX, yAY] = [-xAY, xAX]; // swap and negate new x
// set the transform
ctx.setTransform(xAX, xAY, yAX, yAY, 0, 0);
}
Lets create a function that will draw an image anywhere on the canvas that is rotated and scaled uniformly. We will use the center of the image as the reference point
function drawImageScaleRotate(img, x, y, scale, rotate) {
// define the direction and scale of x axis
const xAX = Math.cos(rotate) * scale;
const xAY = Math.sin(rotate) * scale;
// create the transform with yaxis at 90 CW of x axis and origin at x, y
ctx.setTransform(xAX, xAY, -xAY, xAX, x, y);
// Draw the image so that its center is at the new origin x, y
ctx.drawImage(img, -img.width / 2, -img.height / 2);
}
When we set the transform with ctx.setTranform
we replace the existing transform. This transform remains current. If we use ctx.transform
, ctx.rotate
, ctx.scale
, ctx.translate
the transforms are applied to the current transform, you build a transform in stages.
The transform functions are relatively expensive in terms of CPU cycles. That is way using sin and cos to build the matrix is much faster than using ctx.scale
, ctx.rotate
, ctx.translate
to do the same thing starting from default.
Building transforms can become tricky as we need to keep track of what stage we are at.
We generally only use these function not to transform a single image (text, path, or what ever) but to create linked transforms.
For example a game object like a tank. The body of the tank is transformed (rotated and positioned) then the turret which is rotated with the body but has an additional independent rotation by using ctx.rotate
. Full explanation is beyond the scope of this question.
From all this we can create a simplified function that will draw an image with its center at any location, that is uniformly scaled and rotated
function drawImageScaleRotate(img, x, y, scale, rotate) {
const xAX = Math.cos(rotate) * scale;
const xAY = Math.sin(rotate) * scale;
ctx.setTransform(xAX, xAY, -xAY, xAX, x, y);
ctx.drawImage(img, -img.width / 2, -img.height / 2);
}
To reset the transform to the default use ctx.resetTransform
NOTE not fully supported yet or use ctx.setTransform(1,0,0,1,0,0);
Using the above function is the 2nd fastest way to draw animated rotated scaled images, faster than CSS + HTML or SVG. You can literally fill the screen with animated images.
var w,h; var image = new Image; image.src = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1"; var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); canvas.style.position = "absolute"; canvas.style.top = "0px"; canvas.style.left = "0px"; document.body.appendChild(canvas); const resize = () => { w = canvas.width = innerWidth; h = canvas.height = innerHeight;} const rand = (min,max) => Math.random() * (max ?(max-min) : min) + (max ? min : 0); const DO = (count,callback) => { while (count--) { callback(count) } } resize(); addEventListener("resize",resize); const sprites = []; DO(500,()=>{ sprites.push({ xr : rand(w), yr : rand(h), x : 0, y : 0, // actual position of sprite r : rand(Math.PI * 2), scale : rand(0.1,0.25), dx : rand(-2,2), dy : rand(-2,2), dr : rand(-0.2,0.2), }); }); function drawImage(image, spr){ const xAX = Math.cos(spr.r) * spr.scale; const xAY = Math.sin(spr.r) * spr.scale; ctx.setTransform(xAX, xAY, -xAY, xAX, spr.x, spr.y); ctx.drawImage(image, -image.width / 2, -image.height / 2); } function update(){ var ihM,iwM; ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,w,h); if(image.complete){ var iw = image.width; var ih = image.height; for(var i = 0; i < sprites.length; i ++){ var spr = sprites[i]; spr.xr += spr.dx; spr.yr += spr.dy; spr.r += spr.dr; // keeps images in canvas adds space to all sides so that image // can move completely of the canvas befor warping to other side // I do this to prevent images visualy popping in and out at edges iwM = iw * spr.scale * 2 + w; ihM = ih * spr.scale * 2 + h; spr.x = ((spr.xr % iwM) + iwM) % iwM - iw * spr.scale; spr.y = ((spr.yr % ihM) + ihM) % ihM - ih * spr.scale; drawImage(image,spr); } } requestAnimationFrame(update); } requestAnimationFrame(update);
If you are wondering which is the fastest way to draw animated content. That is via webGL. The above can draw 1000 scaled rotated images on most devices at a good frame rate. WebGL can easily draw 10000 (with extra features eg colored) in the same time.
You can rotate your image easily. You can specify angles by which you wants to apply rotation.
Source : https://www.w3schools.com/cssref/css3_pr_transform.asp
<style> img.a { transform: rotate(180deg); } </style> <img class="a" src="https://picsum.photos/id/237/200/300"/>
use transform "rotate".and use it as below
p{ color:red; font-size:12px; text-align:center; } .rotate1{ transform:rotate(45deg); margin-top:40px; } .rotate2{ transform:rotate(90deg); margin-top:40px; } .rotate3{ transform:rotate(180deg); margin-top:40px; }
<p class="rotate1">ROTATE1</p> <p class="rotate2">ROTATE2</p> <p class="rotate3">ROTATE3</p>
You have ctx.rotate(radians)
function. Read below:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.