围绕一个点旋转画布并获取新的x,y偏移量

4
我正在编写一个使用html5画布的多点触摸拼图游戏,其中您可以围绕某个点旋转拼图。每个拼图都有自己的画布,大小与其边界框相同。当旋转发生时,画布大小必须更改,我能够计算并实现这一点。但是,我无法弄清楚如何找到新的x、y偏移量,如果我要让这个拼图看起来围绕枢轴(第一个触摸点)旋转。

以下图片可以更好地解释我的目标。请注意,枢轴点并不总是中心,否则我就可以将新范围和旧范围之间的差异减半。

enter image description here

因此,我知道原始对象的x、y、宽度、高度、旋转角度、新范围(rotatedWidth、rotatedHeight)和与原始对象相关联的枢轴X、Y。但我不知道如何获得新范围的x/y偏移量(以使其看起来像对象围绕枢轴点旋转)

提前致谢!

3个回答

8
  • 首先我们需要找到从枢轴点到角落的距离。
  • 然后计算枢轴和角落之间的角度。
  • 接下来根据前一个角度和新角度计算绝对角度。
  • 最后计算新的角落位置。

快照
上面的演示截图展示了从枢轴点到角落的直线。
当矩形通过移动旋转时,红色的点是通过计算得出的。

这里举了一个使用绝对角度的例子,但你也可以将其转换为例如旧角度和新角度之间的差异。出于简单起见,我保留了角度作为度数而非弧度。

演示首先使用画布的内部平移和旋转来旋转矩形。然后我们使用纯数学方法来达到与证明我们已经正确计算了角落的新x和y坐标点的相同点。

/// 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;

一开始,您可以通过查看打印的x和y值来验证它们是否与矩形(50,100)的初始角相同。

更新

我错过了“偏移量”这个词...很抱歉,但是您可以计算到每个角的距离,从而获得边界的外限,并且只需根据这些距离值混合和匹配角即可使用最小和最大值。

此处有新的在线演示

Bounds

新部分包括一个函数,将为您提供角落的x和y:

///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};
}

现在只需对每个角运行函数,然后执行最小/最大操作以找到边界:
/// 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);

1
哇,我没想到会得到这么好的答案,非常感谢! - Citizen
现场演示崩溃了 T.T - DarckBlezzer
y = (my + dist * Math.sin(na) + 0.5)|0; 这里的符号(|)是什么意思?我想将其转换为Java,但我不确定这个符号(|)代表什么? - Mubashir Murtaza

0

Here is the way that worked best for me. First I calculate what is the new width and height of that image, then I translate it by half of that amount, then I apply the rotation and finally I go back by the original width and height amount to re center the image.

var canvas = document.getElementById("canvas")
const ctx = canvas.getContext('2d')
drawRectangle(30,30,40,40,30,"black")

function drawRectangle(x,y,width,height, angle,color) {
    drawRotated(x,y,width,height,angle,ctx =>{
        ctx.fillStyle = color
        ctx.fillRect(0,0,width,height)
    })
}
function drawRotated(x,y,width,height, angle,callback)
{
    angle = angle * Math.PI / 180
    const newWidth = Math.abs(width * Math.cos(angle)) + Math.abs(height * Math.sin(angle));
    const newHeight = Math.abs(width * Math.sin(angle)) + Math.abs(height * Math.cos(angle));
    var surface = document.createElement('canvas')
    var sctx = surface.getContext('2d')
    surface.width = newWidth
    surface.height = newHeight
    // START debug magenta square
    sctx.fillStyle = "magenta"
    sctx.fillRect(0,0,newWidth,newHeight)
    // END 
    sctx.translate(newWidth/2,newHeight/2)
    sctx.rotate(angle)
    sctx.translate(-width/2,-height/2)
    callback(sctx)
    ctx.drawImage(surface,x-newWidth/2,y-newHeight/2)
}
#canvas{
border:1px solid black
}
<canvas id="canvas">
</canvas>


0

要围绕画布中心点旋转,您可以使用此函数:

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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接