Canvas Zooming in HTML5 mobile - javascript

I'm currently making a game, and i need to zoom into canvas.
I've read a lot about how to zoom in canvas, with the ctx.scale() propriety, the thing is, I want to zoom with both fingers.
I already have the zoom, but it's zooming from the top/left canvas, and not on the middle of my fingers.
I have the middle point between finger 1 and finger 2, but i don't know how to zoom into that specific middle point !
This exemple pretty sums up what i need (I just need the zoom) :
Zoom Canvas to Mouse Cursor
It's working really fine, but with the wheel !
If any of you as any ideas, I'd be really glad to talk ! :)
Thanks everyone !

I added the touch events ... now you need to find the middle of all points in the evt.touches array (each one has a clientX and clientY, among other properties)
You will also need to keep track the distance beetwen those points in order to change the zoom level.
This bin might help you (check line 46) http://jsbin.com/dived/1/edit?js,output

If you have the center point from which you want to scale, you could use the translation tool provided by the CanvasRenderingContext2D:
void translate(
in float x,
in float y
);
So when you have your canvas context in a variable ctx do
ctx.translate(x, y);
And then
ctx.scale(x, y);
The ctx.translate(x, y) places the origin to x, y coordinates. By default, the origin of the CanvasRenderingContext2D is at 0, 0 (http://www.w3.org/TR/2dcontext/) so that's why the scaling originates from the top left corner.
Source https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D#translate()

Related

Find a point old position after zoom using java script

I am working on this "simple" problem for hours with no success, although I tried many ways to solve it using all kind of solutions suggested in SO.
My problem is as follows:
I have a point on a canvas, which when I click on it my app does something, after identifying the point by comparing the mouse click coordinates to the stored position of the point.
After zooming into the point, using the mouse wheel, I click on the point again but the mouse coordinates no longer fits the stored position of the point.
I need to either transform the mouse coordinates to it's coordinates before the zoom, so I will be able to compare to the stored position, or to transform the stored position to the new canvas so it can be compare to the coordinates of the mouse. Any of the solution is fine by me.
I know the following data:
The "scale" value,
The size of the canvas (top, left, width, height),
The new origin of the canvas (top, left)
I would like a solution using java script.
Finally figured it out and it is quite simple, once I understood the concept.
Save the new canvas origin after doing the zoom (in JS it is calling ctx.translate() and ctx.scale(), where ctx is the canvas context.
When need to calculate the mouse position in the old coordinate system, one has to add back the moved origin of the canvas, and multiply by the scale factor.
seat.x = (-new_org.x + pos.x) / scale;
seat.y = (-new_org.y + pos.y) / scale;
where pos is the calculated mouse pointer
pos.x = event.clientX - .getBoundingClientRect().left;
pos.y = event.clientY - .getBoundingClientRect().top;

Understanding rotation and calculating the top left point in KineticJS

I am working on a page where I can view images. I want to create a rotation tool. I've done that, but, it's not working consistently. When I set up the centre point to rotate by, the image jumps slightly, and it gets worse each time. I was experimenting, and, I have code to add a wedge to the top left corner of my top level group ( so, at 0,0 ). If I rotate the image by 45 degrees and drag it so that half of it is off the left edge of my canvas, then I call getAbsolutePosition on the wedge and on the group, I get these values:
layer.getAbsolutePosition()
Object {x: 104.66479545850302, y: 279.2748571151325}
wedge.getAbsolutePosition()
Object {x: 180.2684127179338, y: -73.48773356791764}
I think this means my y position is actually the bottom of the image, which is off screen.
What I want to do, is calculate the absolute position of the middle of my image, when the mouse moves over it, regardless of it's rotation. I have some code that works out points with rotation, which seems like it works at first, almost, but it just gets more and more broken the more I use the tool. I feel like there's something about how Kinetic is tracking these things and what it's reporting, that I am missing. Any hints would be most appreciated. Tutorials I can read are even better ( yes, I've read everything linked from the KineticJS site and searched the web ).
In a nutshell, the question is, if I have an image inside a group, and it's rotated, how do I work out the centre point of the image, taking the rotation in to account, and how do I set the offset so it will rotate from that point, and stay in the same place ?
Thanks
As you've discovered about KinetiJS:
rotation is easy
dragging is easy
dragging+rotation is difficult
After you drag your image you must reset its rotation point (offsetX/offsetY).
KineticJS makes dragging+rotation more difficult than it has to be.
Resetting the offset points of your image will cause KineticJS to automatically move your image (Noooo!!).
That's what's causing your jumping.
The solution to the "jumping" problem:
When you reset the image's rotation point (offsetX/OffsetY) you must also reset the image's X/Y position.
This code resets both XY and Offsets for an image after dragging:
A Demo: http://jsfiddle.net/m1erickson/m9Nw7/
// calc new position and offset
var pos=rect.getPosition();
var size=rect.getSize();
var offset=rect.getOffset();
var newX=pos.x-offset.x+size.width/2;
var newY=pos.y-offset.y+size.height/2;
// reset both position and offset
rect.setPosition([newX,newY]);
rect.setOffset(size.width/2,size.height/2);

Moving a vector "straight out" along a fixed angle

I've searched for the answer to this and have tried many proposed solutions, but none seem to work. I've been struggling with this forever so any insight is greatly appreciated.
I have 3 shapes (vectors I suppose) on a JS canvas, each with an orientation represented as degrees off of 0 and a width. I need to drag one of these shapes "straight out" from its orientation. This is difficult to explain in words so please view the graphic I created:
The middle (diagonal) shape is at 45 degrees. It's origin is the red dot, (x1,y1). The user drags the shape and their mouse lies at the green dot, (x2,y2). Since the shape's origin is in the lower left, I need to position the shape at the position of the lighter blue shape as if the user has dragged straight outward from the shape's origin.
I don't think it matters, but the library I'm using to do this is KineticJS. Here's the code and some information I have available which may help solve the problem. This code positions the shape on top of the mouse, which isn't what I want:
var rotationDeg = this.model.get("DisplayOri"), // rotation in degrees
rotationRadians = rotationDeg * Math.PI / 180, // rotation in rads
unchanged = this.content.getAbsolutePosition(), // {x,y} of the shape before any dragging
dragBoundFunc = function (changed) {
// called on a mouseMove event, so changed is always different and is the x,y of mouse on stage
var delta = {
x: changed.x - unchanged.x,
y: changed.y - unchanged.y
};
return changed; // go to the mouse position
};
[edit] I should mention that the obvious of "return delta" doesn't work.
It sounds like you want to constrain the movement of the object.
Determine the vector representing the constraint axis : that is, we only want motion to occur along this line. It appears from your drawing that this is in the direction of the short line from the red dot out to the left. That vector has a direction of -1/m where m is the slope of the line we are moving.
Constrain the movement. The movement is represented by the mouse move delta - but we only want the portion of that movement in the direction of the constraint axis. This is done with a dot product of the two vectors.
So in pseudo code
m = (line.y2 - line.y1)/(line.x2 - line.x1)
constraintSlope = -1/m
contraintVector = {1, constraintSlope} //unit vector in that direction
userMove = {x2-x1, y2-y1} //vector of mouse move direction
projection = userMove.x * constraintVector.x + userMove.y * constraintVector.y
translation = projection * constraintVector //scaled vector

Canvas Rotating object about dynamic points

I have a rounded rectangle at a specific x, y, w, h on a canvas. I first do a context.translate to get the object where I want it, then when it comes to rotating it, this is where I'm having issues working out the math needed.
I can do a simple context.rotate(Math.PI/180 * 25) to rotate it 25degs but it rotates from the x,y. I really want to shift the rotating point to like x + (w/2) and y + (w/2).
I'm not sure how to tell the rotate method to rotate it around a different point. I think I have to rotate it like normal but recalculate x,y perhaps based on the rotation maybe?
The canvas always rotates about the origin (0,0). The ctx.translate command can be thought of as shifting the origin, so you must translate by (x+w/2, y+h/2) before you rotate if you wish to rotate about the center of the rectangle.
(and of course, translate back after, or use save and restore)

How to set canvas translation to top left corner after rotation

I'm drawing a symbol with simple lines, but want the user to be able to specify the rotation (in 90degrees only).
Which also means that the dimensions of my canvas change.
No I calculate the dimensions, and set the canvas size. Then I set the center of the rotation to the center of my canvas (via ctx.translate) and rotate to the arbitrary degrees.
Now my problem is: How do I set the translation back to the upper left corner, so I can draw my symbol normally from that position? Do I really have to calculate the values with the rotation?
Thanks.
Great question! translate rotate and scale are all functions that operate upon the current matrix. This gives you lots of options. Probably the simplest thing is to do a save restore on the context matrix
ctx.save();
ctx.translate ( to the center );
ctx.rotate ( do rotation );
//Draw rotated stuff
ctx.restore();
//Draw non-rotated stuff
Now after you call restore, the matrix reverts to how it was before the last save - In opengl, this is actually a stack, and you can push many contexts, but I'm not sure if webgl supports that.
This link may also be helpful: https://developer.mozilla.org/en/drawing_graphics_with_canvas
Hope this helps.
Update:
Yes. Okay, so you are misunderstanding something a little bit. The translations and rotations are applied before the drawing. This is because of a lot of complex math, and is really beyond the scope of this question. So if you want to draw part of your canvas rotated one way, and the other part of it rotated a different way, you first save, then apply the transformations, do the first part of the drawing, then restore to get back to the pre-transformed state. At which point, you can repeat.
So, for example, you can do this:
ctx.save();
ctx.translate ( x_center, y_center );
ctx.rotate ( 90 );
//Draw your rotated stuff starting at the center
ctx.translate ( -x_center, -y_center );
//Draw your main frame stuff that is all rotated around the center
ctx.restore();
ctx.save();
ctx.rotate ( 90 );
//Draw your text which is rotated around the top-left corner
ctx.restore();
In this way, you have 1 drawing function, and you simply setup a context before you draw the different components.

Categories