Canvas - Rotating my images(circles) - javascript

I currently have a bunch of circles that i fill with a image inside a "box", they are bouncing and colliding. Now I want them to rotate.
I have tried to get this to work but I only seem to be able to rotate the entire canvas, I only want the balls to rotate.
This is my render-function:
var img = new Image;
img.onload = function() {
render();
console.log("asdsad");
}
img.src = "xxx.png";
function render() {
var ball;
for (var i = 0; i <balls.length; i++) {
ball = balls[i];
ball.x = ball.nextx;
ball.y = ball.nexty;
context.drawImage(img, ball.x - (img.width/2), ball.y - (img.height/2));
}
}
GOAL: Get these balls to rotate.
EDIT: I have tried something like this, obviously I'm doing something wrong.
context.rotate(Math.PI/180); // Should this be another value? Either way, entire canvas rotates.
context.drawImage(img, ball.x - (img.width/2), ball.y - (img.height/2));
//context.rotate(-Math.PI/180)
context.restore();

You are almost there with your code. You are only forgetting the actual angle:
If you want something easy to wrap the head around and use angles in degrees you can do:
context.rotate(angle * Math.PI / 180);
a trick here is to pre-calculate Math.PI / 180 and use that to calculate:
var deg2rad = Math.PI / 180;
...
context.rotate(angle * deg2rad);
Then draw the balls.
You probably already know but just in case (as it isn't in the example you provided) - To use restore you must first use save:
context.save();
context.rotate(angle * Math.PI / 180);
context.drawImage(img, ball.x - (img.width * 0.5), ball.y - (img.height * 0.5));
context.restore();
Update:
I made an example with a single ball. Just apply the principle for many.
ONLINE DEMO HERE
The main loop goes like this:
/// pre-setup done (see demo)
function loop() {
/// change direction if at some edge
if (x < r || x > ez.width - r) {
dx = -dx;
angleDelta = -angleDelta; /// dummy bounce
}
if (y < r || y > ez.height - r) {
dy = -dy;
}
/// clear previous ball
ctx.clearRect(x - r - 2, y - r - 2, img.width + 4, img.height + 4);
/// move and rotate
x += dx;
y += dy;
angle += angleDelta;
/// save context
ctx.save();
/// move to ball's center position
ctx.translate(x, y);
/// rotate at balls center position
ctx.rotate(angle);
/// draw ball offset so center is center of image
ctx.drawImage(img, -r, -r);
/// reset translation and rotation
ctx.restore();
///again
requestAnimationFrame(loop);
}

You said you have tried:
context.rotate(Math.PI/180);
As known in mathematics PI = 180 degree and if you do PI / 180 = 1 degree it will do nothing except a little.
So if you want to rotate the canvas 90 degree you have to write:
context.rotate(Math.PI/2);
if you want 180 degree you have to:
context.rotate(Math.PI);
and carry on.
with some calculation you will be able to rotate it to any degree you want.
if this does not work you can try this alternative
this function take the arguments and buld the image for you in a simple way that you can understand it might help someone out their.
this function help you
function drawImg(img, pX, pY, oX, oY, w, h, rot , context2D) {
context2D.save();
context2D.translate(pX+oX, pY+oY);
context2D.rotate(rot * Math.PI / 180);
context2D.drawImage(img, 0, 0, w, h, -(oX), -(oY), w, h);
context2D.restore();
}
Summary:
img: the image object
pX: the x position of the image
pY: the y position of the image
oX: how far across the image that the origin is
oY: how far down the image that the origin is
w: width of the image
h: height of the image
rot: angle of rotation
context2D: the context that have been build from the canvas

Related

Rotating coordinates (face landmarks) with origin at top left using canvas

I'm using Google's Cloud Vision API to detect faces and landmarks within them (like eyes, nose and so on).
If the face is rotated, I'd like to correct the rotation so the face and its landmarks are positioned vertically inside a canvas element.
Google provides the coordinates of the landmarks with their origin in the top left, and roll, tilt and pan properties in degrees:
"landmarks": [
{
"position": {
"x": 371.52585,
"y": 437.1983,
"z": 0.0012220144
},
"type": "LEFT_EYE"
},
...
"panAngle": -2.0305812,
"rollAngle": 26.898327,
"tiltAngle": -2.6251676,
...
I can correct the rotation of the image by converting the rollAngle property to radians using ctx.rotate(degrees*Math.PI/180), but how do I rotate the coordinates so they match the rotated image?
My goal is to have the image and the corresponding coordinates as follows:
Cheers
I can see two ways:
Determine basic face image rotation, rotate it to the upright position and then determine face landmarks once again;
or
Use Mathemetics:
x1 = x0 * cos(roll)
y1 = y0 * sin(roll)
I didn't want to have to send two network requests to the API for this. The solution was to rotate both the canvas and the coordinates separately. First I rotate the canvas at its centre, using the rollAngle provided by the Cloud Vision API.
function rotateCanvas(canvas, image) {
let ctx = canvas.getContext('2d');
let rollAngle = sampleResponse.faceAnnotations[0].rollAngle; // rotation of face provided by the Cloud Vision API
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.translate(
canvas.width / 2,
canvas.height / 2);
ctx.rotate(Math.PI / 180 * -rollAngle);
ctx.translate(
-(canvas.width / 2),
-(canvas.height / 2));
ctx.drawImage(
image,
canvas.width / 2 - canvas.width / 2,
canvas.height / 2 - canvas.height / 2,
canvas.width,
canvas.height);
ctx.restore();
return canvas;
}
Next I used this answer to loop through each landmark provided by the Cloud Vision API and rotate it by the given rollAngle:
function rotateCoordinate(cx, cy, x, y, angle) {
// rotate the landmarks provided by the cloud vision API if the face in the supplied
// image isn't vertically aligned
var radians = (Math.PI / 180) * angle,
cos = Math.cos(radians),
sin = Math.sin(radians),
nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
return [nx, ny];
}
As with the canvas rotation, I rotated everything from the centre.
function rotateLandmarks(canvas, landmarks) {
let rollAngle = sampleResponse.faceAnnotations[0].rollAngle;
for (let i = 0; i < landmarks.length; i++) {
let rotated = this.rotateCoordinate(
canvas.width / 2,
canvas.height / 2,
landmarks[i].position.x,
landmarks[i].position.y,
rollAngle)
landmarks[i].position.x = rotated[0];
landmarks[i].position.y = rotated[1];
}
return landmarks;
}

Why are multiple rotate() needed to place every number on the right spot? - Canvas Clock Numbers

Following this tutorial which shows how to make an analog clock using HTML canvas, I've had a hard time understanding what is going on when placing numbers on the clock face.
The code is here, and the following is the part that I'd like to ask.
function drawNumbers(ctx, radius) {
var ang;
var num;
ctx.font = radius * 0.15 + "px arial";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
for(num = 1; num < 13; num++){
ang = num * Math.PI / 6;
ctx.rotate(ang);
ctx.translate(0, -radius * 0.85);
ctx.rotate(-ang);
ctx.fillText(num.toString(), 0, 0);
ctx.rotate(ang);
ctx.translate(0, radius * 0.85);
ctx.rotate(-ang);
}
}
In a for loop, the first ctx.rotate(ang) sets the number on the place it's supposed to be.
The next rotate ctx.rotate(-ang) puts the number back to upright because it's tilted. (although I don't know why it works like this not putting the number back to the first position.)
Then, after ctx.fillText(…) shows the number up, it seems to do the same again.
Why are these two rotate() needed? Do they work differently from the ones in the upper? If do, how?
What this code tries to do is to go back to its previous position, the center of the canvas.
Think of the context as a sheet of paper that you can rotate and move (translate), with a fixed pen over it.
First they do rotate that sheet of paper so that tracing a vertical line will go in the desired direction.
Then they move the sheet of paper vertically, so that the pen is at the correct position. However here, the sheet of paper is still rotated, so if they were to draw the text horizontally from here, the drawing would be oblique.
So they do rotate again in the other way for the text to be at correct angle.
They draw the text.
Now they want to go back to point 1 to be able to draw the next tick. For this they do the same route but in the other way: rotate back the sheet of paper to the desired angle so that they can move vertically to the center.
Move vertically to the center
Finally rotate back so that the sheet of paper is in its original orientation for the next tick.
However you should not do this. rotate() may end up having rounding issues, so doing rotate(angle); rotate(-angle) can not come back to the initial orientation, but to some slightly rotated state, which can be catastrophic for your application since now when you'll try to draw pixel perfect lines, you won't be able and you will kill the whole performances of your app.
Instead use the absolute setTransform method to go back to the original position:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
radius = radius * 0.90
drawNumbers(ctx, radius);
function drawNumbers(ctx, radius) {
var ang;
var num;
ctx.font = radius * 0.15 + "px arial";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
for(num = 1; num < 13; num++){
ang = num * Math.PI / 6;
// go (back) to center
ctx.setTransform(1, 0, 0, 1, radius, radius);
ctx.rotate(ang);
ctx.translate(0, -radius * 0.85);
ctx.rotate(-ang);
ctx.fillText(num.toString(), 0, 0);
}
// reset to identity matrix;
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
canvas {
background-color: white;
}
<canvas id="canvas" width="400" height="400">
</canvas>
Here is another implementation without using rotate.
Instead I calculate the x, y with a bit of trigonometry.
The starting angle is var ang = Math.PI;
Then in the loop we decrease it ang -= Math.PI / 6;
Calculating the position is easy once you know the formula:
let x = radius * Math.sin(ang)
let y = radius * Math.cos(ang)
Below is a fully functional example
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.font = "16px arial";
ctx.textAlign = "center";
var radius = 60
var ang = Math.PI;
for (let num = 1; num < 13; num++) {
ang -= Math.PI / 6;
let x = radius * Math.sin(ang)
let y = radius * Math.cos(ang)
ctx.fillText(num.toString(), x, y);
ctx.beginPath()
ctx.arc(x, y - 6, 12, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath()
ctx.arc(x, y - 6, 45, -ang-2,-ang);
ctx.stroke();
}
<canvas id="canvas" width="160" height="160"></canvas>
I personally never been a fan of using rotate, for a small static canvas image might be fine, but as we move to more complex animations with multiple object, when I have to debug with multiple rotation it quickly becomes painful and quite hard to follow.

Rotating image via javascript

I'm trying to rotate a image using javascript and it should be simple but I can't figure how to draw the image rotated at specific coordinate on the canvas.
Here's the code I found and am trying to use:
ctx.save();
// Translate to the center point of our image
ctx.translate(selectedImg.width * 0.5, selectedImg.height * 0.5);
// Perform the rotation
ctx.rotate( rotAngle * 0.01745 );
// Translate back to the top left of our image
ctx.translate(-selectedImg.width * 0.5, -selectedImg.height * 0.5);
// Finally we draw the image
ctx.drawImage(selectedImg, 0, 0);
// And restore the context ready for the next loop
ctx.restore();
it just rotates the image on top-left corner. how can I draw the image to let's say bottom-right?
As it is documented on MSDN.
The function drawImage has three signatures.
void ctx.drawImage(image, dx, dy);
void ctx.drawImage(image, dx, dy, dWidth, dHeight);
void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
Where d stands for destination and s stands for source.
The solution here should be calculating the image resolution and canvas resolution and make sure to feet the image inside the canvas, then calculate the x,y axes where we should to draw the image.
ctx.save();
// Translate to the center point of our image
ctx.translate(selectedImg.width * 0.5, selectedImg.height * 0.5);
// Perform the rotation
ctx.rotate(rotAngle * 0.01745);
// Translate back to the top left of our image
ctx.translate(-selectedImg.width * 0.5, -selectedImg.height * 0.5);
// Finally we calculate the destination and draw the image
var selectedImgWidth = selectedImg.width;
var selectedImgHeight = selectedImg.height;
var xOffset = selectedImgWidth < canvas.width ? (canvas.width - selectedImgWidth) : 0;
var yOffset = selectedImgHeight < canvas.height ? (canvas.height - selectedImgHeight) : 0;
ctx.drawImage(selectedImg, xOffset, yOffset);
// instead of ctx.drawImage(selectedImg, 0, 0);
// And restore the context ready for the next loop
ctx.restore();
I hope that resolve your issue.
Edit 1: Chenged destination target
This function works for me:
CanvasRenderingContext2D.prototype.drawImageRotated = function (img,x,y,angle)
{
this.save();
var cposX = x + img.width / 2;
var cposY = y + img.height / 2;
this.translate(cposX,cposY); // move canvas center
this.rotate(degToRad(angle));
this.translate(-cposX,-cposY);
this.drawImage(img, x, y);
this.restore();
}

How to make object orbit from behind to front?

Is it possible to make an object orbit around another object that goes from behind and then to the front?
I've seen it being done with rotation animations that do a full 360 around the perimeter, but was wondering if it was possible to do it at an angle.
I couldn't find any resources that could do this, so I've included an image example of what I want to accomplish. The red line would be an object orbiting the blue circle.
Thanks so much - I really appreciate the help!
I figured I'd just write up a solution using the <canvas>
var x, y, scale, state, // Variables we'll use later.
canvas = document.getElementById("canvas"), // Get the canvas,
ctx = canvas.getContext("2d"), // And it's context.
counter = 0, // Counter to increment for the sin / cos functions.
width = 350, // Canvas width.
height = 200, // Canvas height.
centerX = width / 2, // X-axis center position.
centerY = height / 2, // Y-axis center position.
orbit = { // Settings for the orbiting planet:
width: 150, // Orbit width,
height: 50, // Orbit height,
size: 10 // Orbiting planet's size.
};
canvas.width = width; // Set the width and height of the canvas.
canvas.height = height;
function update(){
state = counter / 75; // Decrease the speed of the planet for a nice smooth animation.
x = centerX + Math.sin(state) * orbit.width; // Orbiting planet x position.
y = centerY + Math.cos(state) * orbit.height; // Orbiting planet y position.
scale = (Math.cos(state) + 2) * orbit.size; // Orbiting planet size.
ctx.clearRect(0, 0, width, height); // Clear the canvas
// If the orbiting planet is before the center one, draw the center one first.
(y > centerY) && drawPlanet();
drawPlanet("#f00", x, y, scale); // Draw the orbiting planet.
(y <= centerY) && drawPlanet();
counter++;
}
// Draw a planet. Without parameters, this will draw a black planet at the center.
function drawPlanet(color, x, y, size){
ctx.fillStyle = color || "#000";
ctx.beginPath();
ctx.arc(x || centerX,
y || centerY,
size || 50,
0,
Math.PI * 2);
ctx.fill();
}
// Execute `update` every 10 ms.
setInterval(update, 10);
<canvas id="canvas"></canvas>
If you want to change the roation direction of the orbiting planet, just replace:
x = centerX + Math.sin(state) * orbit.width;
y = centerY + Math.cos(state) * orbit.height;
With:
x = centerX + Math.cos(state) * orbit.width;
y = centerY + Math.sin(state) * orbit.height;
// ^ Those got switched.
The speed of the orbit can be changed by modifying the 75 in:
state = counter / 75;

Rotation logic in html5 canvas

I have this animation, but i cant get over the logic. I hope someone can help me here.
Basicly i need this: http://jsfiddle.net/PDE85/9/ but without the arrow doing such crazy moves. It should be attached to the front of the open circle to simulate an expanding arrow.
I got the triangle to turn right here but it doesnt work when i mix it with position logic as seen in the first example.
Here is the code for reference
(function() {
var size = ($(window).height()/5)*4;
$("#intro-container").css('width',size);
$("#intro-canvas").css('width',size);
$("#intro-canvas").css('height',size);
var interval = window.setInterval(draw, 30);
var degrees = 0.0;
var offset = 20;
var rotate = 0;
var canvas = document.getElementById('intro-canvas');
var ctx = canvas.getContext('2d');
canvas.width = size;
canvas.height = size;
draw();
function draw() {
if (canvas.getContext) {
ctx.fillStyle="white";
ctx.strokeStyle="white";
ctx.clearRect(0, 0, size, size);
ctx.save();
ctx.translate(size/2, size/2);
ctx.rotate(-90 * Math.PI / 180);
ctx.beginPath();
ctx.lineWidth = size/8;
ctx.arc(0, 0, size/3, 0, rotate * Math.PI / 180);
//ctx.shadowBlur=1;
//ctx.shadowColor="black";
ctx.stroke();
ctx.restore();
ctx.beginPath();
ctx.save();
// moving logic
ctx.translate(size/2, size/2);
ctx.rotate(-Math.PI / 180 * -rotate+1);
ctx.translate(-size/3, -size/3);
// rotating logic
ctx.translate(size/2, size/2);
ctx.rotate((rotate * Math.PI + 420) / 180);
ctx.moveTo(0,0);
ctx.lineTo(size/6,0);
ctx.lineTo(0,size/6);
ctx.lineTo(0,0);
ctx.fill();
ctx.restore();
rotate += 1;
if(rotate > 360){
window.clearInterval(interval)
}
}
}
})();
I believe you are looking for this : http://jsfiddle.net/PDE85/12/
The rotation comes from, the rotate call which is unnecessary.
Plus you need an inverted triangle, hence the coordinates needed an update:
...
// ctx.rotate((rotate * Math.PI + 420) / 180);
ctx.moveTo(0,0);
ctx.lineTo(-size/6,0);
ctx.lineTo(0,-size/6);
...

Categories