I have a canvas where I work with an image, move, rotate and scale. Everything is working fine until I try to combine all transformations. For example I'm moving the image based on the user mouse movement difference and it's ok but if I rotate the image first to let's say 180 degrees then the image movement is inverted relative to mouse movement. How can I fix that?
ctx.clearRect(0, 0, 320, 580);
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.scale(scale, scale);
ctx.rotate(toRad(angle));
ctx.translate(-(canvas.width / 2), -(canvas.height / 2));
ctx.drawImage(image, tx, ty, image.width, image.height);
ctx.restore();
Need to post a bit more code if possible, but I believe the issue has to do with your translations. Heres a similiar example, although movement is not dependent on the mouse.
Live Demo
// save the context
ctx.save();
// translate it to the objects x and y, basically your taking the canvas and moving it to each object.
ctx.translate(box.x, box.y);
// now rotate it
ctx.rotate(box.angle);
// -5 is half of the box width and height 0,0 is the boxes location, im drawing it at half the width and height to set the rotation origin to the center of the box.
ctx.fillRect(-5,-5, 10,10);
// now restore
ctx.restore();
Your tx and ty Im assuming are from the mouse coords, well since you have rotated the canvas the tx and ty are now "rotated".
Related
In my Flash program I have a step where I want to rotate a displayObject around the center of its container.
As some of you may know, Flash has a default center point for rotation which is the top left corner, and doesn't fit for my case.
To achieve my specific rotation, I do 3 successive transformations using matrices, like this:
public function rotateAroundCenter(object:DisplayObject, container:DisplayObject, angleDegrees:Number):void {
var matrix:Matrix = object.transform.matrix;
var rect:Rectangle = object.getBounds(container);
matrix.translate(-(rect.left + (rect.width / 2)), -(rect.top + (rect.height / 2)));
matrix.rotate((angleDegrees / 180) * Math.PI);
matrix.translate(rect.left + (rect.width / 2), rect.top + (rect.height / 2));
object.transform.matrix = matrix;
}
This bit of code does the trick and I can rotate displayObjects around their container center like I want to.
Problem: For some of these objects (couldn't find a discriminating factor between those who work and those who don't), any time I try to apply a 180 degrees rotation to put them upside down using previous bit of code, Flash unloads the SWF, which seems very much like a crash to me. I only get this crash for some of these objects, but if they crash once they crash anytime I apply a 180 degrees rotation using my function.
I suspect a memory leak, but then, if 90 or 270 degrees rotations work, why would this specific case make my whole program crash?
Any clues about this issue will be very appreciated. Thanks!
am looking for a solution to realize a spotlight on a html canvas which draws an image. I darkened the complete image with:
context.globalAlpha = 0.3;
context.fillStyle = 'black';
context.fillRect(0, 0, image.width, image.height);
This works fine. Now I want to remove the darkening effect on a specific rectangle area. How can I do this without calculation of the "surrounding" rectangles?
Redraw a region of original image
You can simply draw back the image with the specified rectangular region you want to "reveal":
/// reset global alpha and then
context.globalAlpha = 1;
context.drawImage(image, rx, ry, rw, rh,
rx, ry, rw, rh)
Here you pick first the source rectangle (first line), then the destination rectangle. If width and height (rw and rh) are different the source region is scaled to fit the destination region. If the image size is 1:1 both source and destination would be the same (as in this example).
Clipping
As an alternative you can use clipping instead:
/// to remove clipping properly save current state
context.save();
/// define region you want to clip
context.beginPath();
context.rect(rx, ry, rw, rh);
/// set clip
context.clip();
/// draw the image as the original
context.drawImage(image, x, y);
/// remove clipping
context.restore();
ONLINE DEMO SHOWING THESE TWO TECHNIQUES
Alternatives
Composite modes (globalCompositeOperation) allow clipping as well but would not be useful here as you would still need to draw with a region of the original image and therefor you can just draw it directly (as in first example).
Another way could be to use two canvases where you draw the image on the bottom canvas and the mask on the top canvas. Then punch a hole in the top canvas. If you want to animate/move around the mask this can be a good solution.
I have some simple flash animations that I am converting into HTML5 image maps, that trigger some drawing upon <area> rollovers.
Problem being is that all the circles (representing roll-over points) are given to me as flash circle object coordinates. The points in question are formed at the joining of imaginary lines that go from the circle’s topmost and leftmost points (marked by the dashed red lines in the image below).
But to draw my circles in HTML5 (using raphael.js currently), I have to to give a center coordinate: var c = paper.circle(x, y, radius).
So, for example, if I have flash circle object (532.20,30.35) with a height and width of 19.80 (again from the point where the imaginary lines intersect) how can I calculate the values I need for drawing a circle at its center point in the canvas?
You just subtract the radius from the x and y:
x = x - radius;
y = y - radius;
In your case:
radius = 19.8 * 0.5
x = 532.2 - radius;
y = 30.35 - radius;
This will draw the circle center at the original x and y (it moves the circle left and up).
If you want the circle in the effectively same position as the original but have to move the coordinate system then you add radius instead.
I cannot figure out how to do this. I was translating the character and background at the same time, but if there's any hiccup, the character position slides out of the viewable area of the canvas, and I need the canvas translation to be based off the position of the player (hero.x, hero.y).
Currently I have
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 640;
canvas.height = 480;
//then in my update function
if (38 in keysDown && hero.y > 0){ //UP KEY PRESSED KEY
ctx.translate(0,12); //translate background position +12y
hero.y -= hero.speed * modifier; //move player up on the background image
else if (40 in keysDown && hero.y < 3750-64){ //DOWN KEY PRESSED
ctx.translate(0, -12);
hero.y += hero.speed * modifier;
}
}
That moves the player and the canvas but not guaranteed together...if it freezes at all, the player is off center or even off screen.
The viewable canvas area is 640x480, but the background image you can navigate on is 5,000 x 3750.
On the web browser, when it doesn't freeze, it works how I want, moving the player and background at the same pace as the character.
However, that same rate on the phone puts the player much faster than the screen translates which means the player walks right out of the viewable area even though it still moves the background.
If I do ctx.translate(hero.x, hero.y) and use the hero.x, hero.y coordinates of the player, or some variation of it minus an offset, it moves the background BY that measurement each time I press the key instead of moving it TO that position.
How can I make everything conditional on the players position to move both the player and background, but together, or automatically adjust next update() to center on the player....?????
How can I make everything conditional on the players position to move both the player and background, but together, or automatically adjust next update() to center on the player
Well, the easy way would be to actually always draw the player in the center! In other words, never ever change or translate his or her coordinates. Instead worrying about translating everything else in relation to that.
Since you want the player to always be in the center, you should always draw the player at the center of the canvas (640/2 x 480/2 for you)
Then you'll want to do is keep a canvas offset for X and Y and draw everything (Background, etc) using that offset, then reset the transformation and draw the hero in the plain old center.
So your draw function might look something like this:
function draw() {
ctx.clearRect(0,0,500,500);
// Save the default transformation matrix
ctx.save();
// Translate by the background's offset
ctx.translate(xoffset, yoffset);
// Draw the background (and maybe other things) translated
ctx.fillStyle = backgroundGradient;
ctx.fillRect(0,0,500,500);
// We restore to the default transformation
// This will draw the hero not translated at all
// That means we can always draw the hero in the center!
ctx.restore();
// hero is a black rectangle
ctx.fillRect(240, 240, 20, 20);
}
Here is a live example that works with mouse down and up. There is a big "sun" for the background that moves, while the "stays" rectangle stays still because it is literally always drawn in the same place:
http://jsfiddle.net/3CfFE/
Rotate the dial on top of a semi circular(Northern Hemisphere) image as background.
range could be 0 - 180 degrees.
on input to the method that does canvas transformation, the dial would rotate and stop over the matched value.
Here's what I was trying based on help and sample passed on by phrogz
In general, what you want to do is:
Transform the context to the point on the canvas that the object should rotate about.
Rotate the context.
Transform the context by the negative offset within the object for the center of rotation.
Draw the object at 0,0.
In code:
ctx.save();
ctx.translate( canvasRotationCenterX, canvasRotationCenterY );
ctx.rotate( rotationAmountInRadians );
ctx.translate( -objectRotationCenterX, -objectRotationCenterY );
ctx.drawImage( myImageOrCanvas, 0, 0 );
ctx.restore();
Here's a working example showing this in action. (The math for the rotation was just experimentally hacked to find a swing amount and offset in radians that fit the quickly-sketched gauge dial.)
As may be evident, you can substitute the translate call in step #3 above with offsets to the drawImage() call. For example:
ctx.drawImage( myImageOrCanvas, -objectRotationCenterX, -objectRotationCenterY );
Using context translation is recommended when you have multiple objects to draw at the same location.