I need help, I have been trying to rotate my image which is a pointer. with my code below it rotates but it rotates not as I want.
Code:
var c = document.getElementById("ctx");
var ctx = c.getContext("2d");
for (var d = 0; d < 360; d++) {
setTimeout(function (d) {
c.globalAlpha = 0.5;
ctx.clearRect(0, 0, c.width, c.height);
ctx.save();
ctx.translate(c.width / 2, c.height / 2);
ctx.rotate(d * Math.PI / 180);
ctx.drawImage(imageLoader.pointer, -imageLoader.pointer.width / 2, -imageLoader.pointer.height / 2);
ctx.restore();
}, 100 * d, d);
}
This code makes my image rotate weirdly I think it rotates on its own axis but I am not sure.
However I need a rotation something like this image.
I think this rotation is around a circle, i need something like this , can someone give me a hint or help me out?
I was trying to do it with a shape but its more difficult because i need to find the tangent and more geometric formulas to make it rotate like this.
I appreciate your time in advance, thanks.
The function to draw a rotated image rotating around a point on the canvas and offset to its own center of rotation.
// x,y is the location on the canvas that the image will rotate around
// cx,cy is the coordinates on the image that is rotated around
// angle is the amount of rotation in radians
function drawImage(image,x,y,cx,cy,angle){
ctx.setTransform(1,0,0,1,x,y); // set the rotation origin
ctx.rotate(angle); // rotate
ctx.drawImage(image,-cx,-cy); // draw image offset to put cx,cy at the point of rotation
ctx.setTransform(1,0,0,1,0,0); // restore the transform
}
So if your image is 50 by 100 pixels and you want the image to rotate about the point on it at 25, 80 (center near bottom) and that rotation point to be on the canvas at 200,200 then
drawImage(image,200,200,25,80,3); //rotate 3 radians
To do so in an animation.
// assumes image has loaded and ctx is the context of the canvas.
requestAnimationFrame(mainLoop); // starts the animation
function mainLoop(time){ // time is passed by requestAnimationFrame
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); // clear
drawImage(image,200,200,25,80,(time / 5000) * Math.PI * 2); // rotate one every 5 seconds
requestAnimationFrame(mainLoop);
}
const image = new Image
image.src = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1";
const ctx = myCanvas.getContext("2d");
function drawImage(image,x,y,cx,cy,angle){
ctx.setTransform(1,0,0,1,x,y); // set the rotation origin
ctx.rotate(angle); // rotate
ctx.drawImage(image,-cx,-cy); // draw image offset to put cx,cy at the point of rotation
ctx.setTransform(1,0,0,1,0,0); // restore the transform
}
// assumes image has loaded and ctx is the context of the canvas.
requestAnimationFrame(mainLoop); // starts the animation
function mainLoop(time){ // time is passed by requestAnimationFrame
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); // clear
if(image.complete){
drawImage(image,250,250,image.width / 2,image.height * 0.8,(time / 5000) * Math.PI * 2); // rotate one every 5 seconds
}
requestAnimationFrame(mainLoop);
}
canvas {
border : 2px black solid;
}
<canvas id="myCanvas" width = 500 height = 500></canvas>
Related
I was trying to get the green triangle to rotate about its center and orient itself towards the mouse position. I was able to accomplish this, and you can view the full code and result here:
https://codepen.io/Carpetfizz/project/editor/DQbEVe
Consider the following lines of code:
r = Math.atan2(mouseY - centerY, mouseX - centerX)
ctx.rotate(r + Math.PI/2)
I arbitrarily added Math.PI/2 to my angle calculation because without it, the rotations seemed to be 90 degrees off (by inspection). I want a better understanding of the coordinate system which atan2 is being calculated with respect to so I can justify the reason for offsetting the angle by 90 degrees (and hopefully simplify the code).
EDIT:
To my understanding, Math.atan2 is measuring the angle illustrated in blue. Shouldn't rotating both triangles that blue angle orient it towards the mouse mouse pointer (orange dot) ? Well - obviously not since it's the same angle and they are two different orientations, but I cannot seem to prove this to myself.
This is because of how the Math.atan2 works.
From MDN:
This is the counterclockwise angle, measured in radians, between the positive X axis, and the point (x, y).
In above figure, the positive X axis is the horizontal segment going from the junction to the right-most position.
To make it clearer, here is an interactive version of this diagram, where x, y values are converted to [-1 ~ 1] values.
const ctx = canvas.getContext('2d'),
w = canvas.width,
h = canvas.height,
radius = 0.3;
ctx.textAlign = 'center';
canvas.onmousemove = canvas.onclick = e => {
// offset mouse values so they are relative to the center of our canvas
draw(as(e.offsetX), as(e.offsetY));
}
draw(0, 0);
function draw(x, y) {
clear();
drawCross();
drawLineToPoint(x, y);
drawPoint(x, y);
const angle = Math.atan2(y, x);
drawAngle(angle);
writeAngle(angle);
}
function clear() {
ctx.clearRect(0, 0, w, h);
}
function drawCross() {
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(s(0), s(-1));
ctx.lineTo(s(0), s(1));
ctx.moveTo(s(-1), s(0));
ctx.lineTo(s(0), s(0));
ctx.strokeStyle = ctx.fillStyle = '#2e404f';
ctx.stroke();
// positive X axis
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(s(0), s(0));
ctx.lineTo(s(1), s(0));
ctx.stroke();
ctx.lineWidth = 1;
ctx.font = '20px/1 sans-serif';
ctx.fillText('+X', s(1) - 20, s(0) - 10);
}
function drawPoint(x, y) {
ctx.beginPath();
ctx.arc(s(x), s(y), 10, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.fill();
ctx.font = '12px/1 sans-serif';
ctx.fillText(`x: ${x.toFixed(2)} y: ${y.toFixed(2)}`, s(x), s(y) - 15);
}
function drawLineToPoint(x, y) {
ctx.beginPath();
ctx.moveTo(s(0), s(0));
ctx.lineTo(s(x), s(y));
ctx.strokeStyle = 'red';
ctx.setLineDash([5, 5]);
ctx.stroke();
ctx.setLineDash([0]);
}
function drawAngle(angle) {
ctx.beginPath();
ctx.moveTo(s(radius), s(0));
ctx.arc(s(0), s(0), radius * w / 2,
0, // 'arc' method also starts from positive X axis (3 o'clock)
angle,
true // Math.atan2 returns the anti-clockwise angle
);
ctx.strokeStyle = ctx.fillStyle = 'blue';
ctx.stroke();
ctx.font = '20px/1 sans-serif';
ctx.fillText('∂: ' + angle.toFixed(2), s(0), s(0));
}
// below methods will add the w / 2 offset
// because canvas coords set 0, 0 at top-left corner
// converts from [-1 ~ 1] to px
function s(value) {
return value * w / 2 + (w / 2);
}
// converts from px to [-1 ~ 1]
function as(value) {
return (value - w / 2) / (w / 2);
}
<canvas id="canvas" width="500" height="500"></canvas>
So now, if we go back to your image, it currently points to the top (positive Y axis), while the angle you just measured is realtive to the x axis, so it doesn't point where you intended.
Now we know the problem, the solution is quite easy:
either apply the + Math.PI / 2 offset to your angle like you did,
either modify your original image so that it points to the positive X axis directly.
The coordinate system on canvas works with 0° pointing right. This means anything you want to point "up" must be initially drawn right.
All you need to do in this case is to change this drawing:
to
pointing "up" 0°
and you can strip the math back to what you'd expect it to be.
var ctx = c.getContext("2d"), img = new Image;
img.onload = go; img.src = "https://i.stack.imgur.com/Yj9DU.jpg";
function draw(pos) {
var cx = c.width>>1,
cy = c.height>>1,
angle = Math.atan2(pos.y - cy, pos.x - cx);
ctx.setTransform(1,0,0,1,cx, cy);
ctx.rotate(angle);
ctx.drawImage(img, -img.width>>1, -img.height>>1);
}
function go() {
ctx.globalCompositeOperation = "copy";
window.onmousemove = function(e) {draw({x: e.clientX, y: e.clientY})}
}
html, body {margin:0;background:#ccc}
#c {background:#fff}
<canvas id=c width=600 height=600></canvas>
When you do arctangents in math class, you're generally dealing with an y-axis that increases going upwards. In most computer graphics systems, however, including canvas graphics, y increases going downward. [erroneous statement deleted]
Edit: I have to admit what I wrote before was wrong for two reasons:
A change in the direction of the axis would be compensated for by adding π, not π/2.
The canvas context rotate function rotates clockwise for positive angles, and that alone should compensate for the flip of the y-axis.
I played around with a copy of your code in Plunker, and now I realize the 90° rotation simply compensates for the starting orientation of the graphic image you're drawing. If the arrowhead pointed right to start with, instead of straight up, you wouldn't need to add π/2.
I encountered the same problem and was able to achieve the desired result with a following axis 'trick':
// Default usage (works fine if your image / shape points to the RIGHT)
let angle = Math.atan2(delta_y, delta_x);
// 'Tricky' usage (works fine if your image / shape points to the LEFT)
let angle = Math.atan2(delta_y, -delta_x);
// 'Tricky' usage (works fine if your image / shape points to the BOTTOM)
let angle = Math.atan2(delta_x, delta_y);
// 'Tricky' usage (works fine if your image / shape points to the TOP)
let angle = Math.atan2(delta_x, -delta_y);
I have a code sample where I am adjusting a large image to a smaller size with the canvas matching the size of the image. There is a rotate right functionality that will rotate the image inside of the canvas which will adjust the canvas size. I almost have it perfectly working but the image is perfect size only when original or upside down. I can't get the height to be right ratio when the image is rotated on left or right. Here is my jsfiddle and code which is modified code from a different fiddle. Image is just used as test.
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvasWidth = 400;
var canvasHeight;
var degrees=0;
var image=document.createElement("img");
image.onload=function(){
canvas.width = canvasWidth;
canvasHeight = 400 / (image.width / image.height);
canvas.height = canvasHeight;
ctx.drawImage(image,0,0,canvas.width,canvas.height);
}
image.src="https://www.magezinepublishing.com/equipment/images/equipment/Lumix-DMCFZ330-5824/highres/Panasonic-Lumix-FZ330-Wide-P1010001_1438873612.jpg";
$("#clockwise").click(function(){
degrees+=90
if (degrees >= 360) degrees = 0;
if (degrees === 0 || degrees === 180 ) {
canvas.width = canvasWidth;
canvas.height = canvasHeight;
}
else {
// swap
canvas.width = canvasHeight;
canvas.height = canvasWidth;
}
ctx.save();
// you want to rotate around center of canvas
ctx.translate(canvas.width/2,canvas.height/2);
ctx.rotate(degrees*Math.PI/180);
ctx.drawImage(image, -canvas.width*0.5, -canvas.height*0.5, canvas.width, canvas.height);
ctx.restore();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" ></canvas><br>
<button id="clockwise">Rotate right</button>
jsfiddle
Any help is appreciated.
Remember you rotate your image. You are changing the aspect ratio of the image when using the swapped canvas width/height in the image draw call. By your calculations, the original image, rotated, should already fit the canvas. The image you produce is also stretched because of this.
Use ctx.drawImage(image, -canvasWidth*0.5, -canvasHeight*0.5, canvasWidth, canvasHeight);.
Else said, you want to draw the same image, then rotate/translate it with canvas calls. But what you are doing is draw a different sized, stretched image when being rotated 90° or 270° and then rotate/translate that.
To rotate an image in 90deg steps.
Rather the use the translate and scale you can rotate the image by directly setting the tranformation matrix.
ctx.setTransform(a,b,c,d,e,f);
The arguments are as follows
a,b the vector describing the direction and size of a pixels x axis
c,d the vector describing the direction and size of a pixels y axis
e,f the coordinate of the origin (where 0,0 will be)
All these are in canvas pixel coordinates. The default is a = 1, b =0 x axis of a pixel is 1 pixel across, and 0 down, c = 0, d = 1 y axis 0 pixels across and one pixel down, e = 0, f = 0 origin at the top left.
To rotate the image 90 deg clockwise you want the xAxis to go down the canvas and the y axis to go from right to left. The origin is shifted to the top right of the canvas.
ctx.setTransform(
0,1, // x axis down
-1,0 // y axis from left to right
ctx.canvas.height, // origin x and y to top right
0,
)
ctx.drawimage(image,0,0);
As you are scaling by 0.5 this means the pixels will be half the size, and as you are drawing an image you want the origin so that the image fits the image.
// rotate image 90 deg and scale 0.5
ctx.setTransform(
0,0.5, // x axis down
-0.5,0 // y axis from left to right
image.height * 0.5, // origin x and y to top right
0,
)
ctx.drawimage(image,0,0);
You can do the same for each additional rotation
// rotate 180 scale 0.5
ctx.setTransform(-0.5,0,0,-0.5, image.width * 0.5,image.height * 0.5);
// rotate -90 scale 0.5
ctx.setTransform(0,-0.5,0.5,0, 0,image.width* 0.5);
The image dimensions for each rotation is as follows
// for 0deg and 180 deg rotation
canvas.width = image.width * 0.5;
canvas.width = image.height * 0.5;
// for 90deg and -90 deg rotation
canvas.width = image.height * 0.5;
canvas.width = image.width * 0.5;
To restore the transform to the default just set the transform to the identity matrix
ctx.setTransform(1,0,0,1,0,0);
I'm trying to make a pinball game and wondering the logic of the pinball flipper motion.
On the even handler of a right arrow, I want to move my rectangular piece several degrees up.
ctx.rotate(-(20*Math.PI/180));
ctx.beginPath();
ctx.rect(this.rposX , this.rposY, this.width, this.height);
ctx.fillStyle = 'red';
ctx.fill();
ctx.closePath();
ctx.rotate(20*Math.PI/180);
if (rPressed) {
this.flipRight(ctx);
}
Is what I have. How should I try to flip the flipper. Sometimes, I rotate it but that rotates all the objects.
Rotating a rendering
To do the flippers you create a function that draws a flipper at the origin (0,0) and along the xAxis left to right.
So from your rectangle example rather than draw the rectangle where the flipper is, draw it so that the canvas coords 0,0 is at the point of rotation. You will move it via the transform to the correct position.
ctx.strokeRect(-10,-10,100,20); // 0,0 is point of rotation
You position the flipper by moving its center point setTransform I use setTransform as it save having to use save and restore.
// x,y is the position you want the flipper to be.
ctx.setTransform(1,0,0,1,x,y); // sets position of flipper by moving point of rotation
And then rotate with
ctx.rotate(angle); // angle is in radians
The just draw the flipper
ctx.strokeRect(-10,-10,100,20); // 0,0 is point of rotation
Keyboard events
To animate I draw the flippers 60 times a second. I have two event listeners listen to keydown and keyup events. When a key is down I set the flipper flag on to true and when key up to false. I don't do any other processing in the key events.
See demo for more details on key event listeners
Animating the flippers
In the animation loop I call the flipper update function. It checks if the flipper is on or off and then moves the flipper depending on its state. This function is called once for every animation frame.
Example
I have not done a flipper or pinball game in a long time so I had a bit of fun and created some working flippers.
The function you want is called drawFlipper it has comments. The whole lot is animated using requestAnimationFrame
// get the canvas context
const ctx = canvas.getContext("2d");
// defines the flipper
const flipper = {
on : false, // on when key down
size : 20, // radius of flipper base
pos : 0.1,
shapeAng : Math.PI * 0.4, // approx angle to pointy end that line starts at the big arc
point : 5, // radius of pointy end
length : 100, // length of flipper
action : 0, // action is the rotational position
actionDest : 0, // is current destination where flipper should be
actionV : 0.0, // is movement towards destination
// next two numbers v must be 0 < v < 1
actionAccel : 0.7, // relative acceleration of flipper for action. bigger number faster flipper
actionDrag : 0.61, // amount of drag on flipper action. Smaller number more drag
actionExtent : 1, // max travel of flipper
actionBumperDamp : 0.8, // Amount of dampening flipper stop has
update(){
if(this.on){
this.actionDest = this.actionExtent;
}else{
this.actionDest = 0; // home position
}
this.actionV += (this.actionDest - this.action) * this.actionAccel;
this.actionV *= this.actionDrag
this.action += this.actionV
if(this.action > this.actionExtent){
this.action = this.actionExtent;
this.actionV *= this.actionBumperDamp;
this.actionV -= this.actionV;
}
}
}
// keyboard listeners
document.addEventListener("keydown",(e)=>{
flipper.actionDrag = Number(dragIn.value);
flipper.actionAccel = Number(accelIn.value);
flipper.on = true
});
document.addEventListener("keyup",(e)=>{ flipper.on = false});
window.focus(); // get the keyboards attention
// draws a flipper
// x,y is the location of the flipper base
// start angle is the home angle of the flipper
// flipperTravelDirection is flipper travel direction when active
function drawFlipper(x,y,startAng,flipperTravelDirection){
// set the position
ctx.setTransform(1,0,0,1,x,y);
// set the angle of the flipper plus action position
ctx.rotate(startAng + flipper.action * flipperTravelDirection);
// draw the flipper. the transform sets position and rotation
// so just have to render the flipper around its own center 0,0
ctx.beginPath();
ctx.arc(0, 0, flipper.size,flipper.shapeAng, Math.PI * 2 - flipper.shapeAng);
ctx.lineTo(flipper.length, - flipper.point);
ctx.arc(flipper.length,0,flipper.point,-Math.PI / 2,Math.PI /2)
ctx.closePath();
ctx.stroke();
}
var W,H; // canvas width and height
// draws the flippers. This function would do all rendering of the
// game animations
function mainLoop(time){
// resize if needed
if(canvas.width !== innerWidth || canvas.height !== innerHeight){ // resize canvas if window size has changed
W = canvas.width = innerWidth;
H = canvas.height = innerHeight;
}
// clear canvas
ctx.setTransform(1,0,0,1,0,0); // set default transform
ctx.clearRect(0,0,W,H); // clear the canvas
// update flipper actions
flipper.update();
// render the flippers left and right
drawFlipper(flipper.size * 2, H / 2, Math.PI * 0.25,-1);
drawFlipper(W - flipper.size * 2, H / 2, Math.PI * 0.75,1);
// get next animation loop
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
canvas {
position : absolute;
top : 0px;
left : 0px;
z-index : -10;
}
body {font-family : arial;}
Press any key to move flippers. Move sliders to change flipper acction.<br>
Left slider is acceleration, right is drag. Some settings make them flip out, change setting and hit key to fix<br>
Accel<input id="accelIn" type="range" min = 0.01 max = 0.99 step = 0.01 value = 0.5></input>
Drag<input id="dragIn" type="range" min = 0.01 max = 0.99 step = 0.01 value = 0.5></input>
<canvas id=canvas></canvas>
I'm trying to rotate an image and move it around the canvas using the arrow keys. The plan is to have the left and right keys control the rotation of the image, and the up-down key control the movement - a bit like a tank!
I can successfully rotate an image around a centre point and place it back where it should be in the canvas, but once I rotate it by, say 45 deg, I would like the up key to move it right, rotate 180 and the up-key moves it down the canvas etc. At the moment, I can rotate the image using left/right keys, but up/down keys are always up/down the canvas.
Do I somehow need to rotate the canvas coordinates by the same amount as the image?
This is what I have so far and is in my draw function…
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.translate(T1.x + base_image.width/2, T1.y + base_image.height/2);
ctx.rotate(rotation * Math.PI/180);
ctx.translate(-(T1.x + base_image.width/2), -(T1.y + base_image.height/2));
ctx.drawImage(base_image, T1.x, T1.y);
ctx.stroke();
ctx.restore()
T1.x and T1.y are the x and y coordinates of the image.
Thanks!
Finally got it! The solution was to separate the rotation and the movement rather than trying to do it all using ctx.translate.
In my draw function called every 100 Hz:
ctx.save();
ctx.translate(T1.x + base_image.width/2, T1.y + base_image.height/2);
ctx.rotate(rotation*Math.PI/180);
ctx.drawImage(base_image, -base_image.width/2, -base_image.height/2);
ctx.restore();
ctx.stroke();
The left key is, for example, like:
rotation = rotation - 5;
Draw();
The up key is, for example, like:
var radians = (rotation*Math.PI/180)
T1.x += 4*Math.cos(radians);
T1.y += 4*Math.sin(radians);
Draw();
Note: For this to work, I had to change the default orientation of the image in paint by 45 deg.
The quickest way to draw a rotated, uniformly-scaled image on Edge, Chrome, and Firefox is
// When the image has loaded add the center offsets
image.onload = function(){
// … your code
this.centerX = -this.width/2;
this.centerY = -this.height/2;
}
// When rendering
// x,y position of image center
// scale the uniform scale
// rotate angle in radians starting at 0 and + clockwise
var xdx = Math.cos(rotate) * scale;
var xdy = Math.sin(rotate) * scale;
ctx.setTransform(xdx, xdy, -xdy, xdx, x, y);
ctx.drawImage(image,image.centerX,image.centerY)
The above method with the extra sin and cos and the two multiplications is significantly quicker on Chrome, slightly quicker on Firefox, and I forget the margin on Edge, but it was quicker than the next quickest method:
ctx.setTransform(scale, 0, 0, scale, x, y);
ctx.rotate(rotate);
ctx.drawImage(image, image.centerX, image.centerY);
Though I am leaving the canvas transform state as it is, you can continue using both methods without having to reset the canvas state. When you are done with all the rendering, to restore the current transform, use
ctx.setTransform(1, 0, 0, 1, 0, 0);
To save a tiny bit of time, each time you convert from degrees to radians, create a const DEG2RAD = Math.PI/180;. Then, you can convert with var radians = degrees*DEG2RAD; or save by typing const D2R = Math.PI/180; or call const oneDeg = Math.PI/180;.
I am working on a program in Javascript while I tried to rotate my image I have drawn. I tried to search on google to find my answer but all I got was how to rotate the whole canvas. What I am searching for is a way to rotate just an image (think like this. I want to rotate a warrior dependent on the direction he is walking in). I tried many different codes but all went to the same, rotate the whole canvas.
Here is an example of what I used:
ctx.rotate(20*Math.PI/180);
and also:
var angle = 0; //init angle
images[0].onload = function () {
ctx.drawImage(images[0], 0, 0);
setInterval(function () {
ctx.save();
ctx.clearRect(-ctx.canvas.width / 2, -ctx.canvas.height / 2, ctx.canvas.width, ctx.canvas.height);
ctx.rotate(Math.PI / 180 * (angle += 10)); //rotating at 10 degrees interval..
ctx.drawImage(images[0], 0, 0);
ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
ctx.restore();
}, 16);
}
please help me out
The rotating of the canvas is part of how to create the rotation relative to the image.
To keep the image's visible position the same and not move it, you have to center it first.
Without centering the image you will only acheive the visual effect of rotating the entire canvas.
You have to throw in a bit of math to get it to rotate around the center of the image.
ctx.save();
ctx.translate(x, y); /*X and Y of the image, center point of the image */
ctx.rotate((Math.PI / 180) * rotation); /*rotation is in degrees, so this converts it to rads */
ctx.drawImage(img, -(img.width/2), -(img.height/2)); /* Draw and center the image */
ctx.restore();