4

I am writing a multitouch jigsaw puzzle using html5 canvas in which you can rotate the pieces around a point. Each piece has their own canvas the size of their bounding box. When the rotation occurs, the canvas size must change, which I am able to calculate and is working. What I can't figure out, is how to find the new x,y offsets if I am to have this appear to be rotating around the pivot (first touch point).

Here is an image to better explain what I'm trying to achieve. Note the pivot point is not always the center, otherwise I could just halve the difference between the new bounds and the old.

enter image description here

So I know the original x, y, width, height, rotation angle, new bounds(rotatedWidth, rotatedHeight), and the pivot X,Y relating to original object. What I can't figure out how to get is the x/y offset for the new bounds (to make it appear that the object rotated around the pivot point)

Thanks in advance!

4

2 回答 2

8
  • First we need to find the distance from pivot point to the corner.
  • Then calculate the angle between pivot and corner
  • Then calculate the absolute angle based on previous angle + new angle.
  • And finally calculate the new corner.

Snapshot from demo
Snapshot from demo below showing a line from pivot to corner.
The red dot is calculated while the rectangle is rotated using
translations.

Here is an example using an absolute angle, but you can easily convert this into converting the difference between old and new angle for example. I kept the angles as degrees rather than radians for simplicity.

The demo first uses canvas' internal translation and rotation to rotate the rectangle. Then we use pure math to get to the same point as evidence that we have calculated the correct new x and y point for corner.

/// find distance from pivot to corner
diffX = rect[0] - mx; /// mx/my = center of rectangle (in demo of canvas)
diffY = rect[1] - my;
dist = Math.sqrt(diffX * diffX + diffY * diffY); /// Pythagoras

/// find angle from pivot to corner
ca = Math.atan2(diffY, diffX) * 180 / Math.PI; /// convert to degrees for demo

/// get new angle based on old + current delta angle
na = ((ca + angle) % 360) * Math.PI / 180; /// convert to radians for function

/// get new x and y and round it off to integer
x = (mx + dist * Math.cos(na) + 0.5)|0;
y = (my + dist * Math.sin(na) + 0.5)|0;

Initially you can verify the printed x and y by seeing that the they are exact the same value as the initial corner defined for the rectangle (50, 100).

UPDATE

It seems as I missed the word in: offset for the new bounds... sorry about that, but what you can do instead is to calculate the distance to each corner instead.

That will give you the outer limits of the bound and you just "mix and match" the corner base on those distance values using min and max.

New Live demo here

Bounds

The new parts consist of a function that will give you the x and y of a corner:

///mx, my = pivot, cx, cy = corner, angle in degrees
function getPoint(mx, my, cx, cy, angle) {

    var x, y, dist, diffX, diffY, ca, na;

    /// get distance from center to point
    diffX = cx - mx;
    diffY = cy - my;
    dist = Math.sqrt(diffX * diffX + diffY * diffY);
    
    /// find angle from pivot to corner
    ca = Math.atan2(diffY, diffX) * 180 / Math.PI;
  
    /// get new angle based on old + current delta angle
    na = ((ca + angle) % 360) * Math.PI / 180;
    
    /// get new x and y and round it off to integer
    x = (mx + dist * Math.cos(na) + 0.5)|0;
    y = (my + dist * Math.sin(na) + 0.5)|0;
    
    return {x:x, y:y};
}

Now it's just to run the function for each corner and then do a min/max to find the bounds:

/// offsets
c2 = getPoint(mx, my, rect[0], rect[1], angle);
c2 = getPoint(mx, my, rect[0] + rect[2], rect[1], angle);
c3 = getPoint(mx, my, rect[0] + rect[2], rect[1] + rect[3], angle);
c4 = getPoint(mx, my, rect[0], rect[1] + rect[3], angle);

/// bounds
bx1 = Math.min(c1.x, c2.x, c3.x, c4.x);
by1 = Math.min(c1.y, c2.y, c3.y, c4.y);
bx2 = Math.max(c1.x, c2.x, c3.x, c4.x);
by2 = Math.max(c1.y, c2.y, c3.y, c4.y);
于 2013-10-16T04:18:13.330 回答
0

to rotate around the centre point of the canvas you can use this function:

function rotate(context, rotation, canvasWidth, canvasHeight) {
    // Move registration point to the center of the canvas
    context.translate(canvasWidth / 2, canvasHeight/ 2);

    // Rotate 1 degree
    context.rotate((rotation * Math.PI) / 180);

    // Move registration point back to the top left corner of canvas
    context.translate(-canvasWidth / 2, -canvasHeight/ 2);
}
于 2019-10-12T11:36:14.170 回答