调整宽度时如何防止旋转的矩形移动?
How to prevent a rotated rectangle from moving when adjusting its width?
我有一个围绕其中心旋转的矩形,我正在尝试使其变宽。然而,因为它绕着它的中心旋转,使它变宽也在视觉上“移动”了矩形。
我正在寻找的是一种使其变宽然后相应地调整其 x 和 y 分量以使其在视觉上不移动的方法。我在这里创建了问题的重现,所以你可以看到问题:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
function rotateCanvas(x, y, a) {
ctx.translate(x, y);
ctx.rotate(a);
ctx.translate(-x, -y);
}
function drawRotateRectangle(x, y, w, h, a) {
rotateCanvas(x + w / 2, y + h / 2, a);
ctx.fillRect(x, y, w, h);
rotateCanvas(x + w / 2, y + h / 2, -a);
}
let which = true;
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const rWidth = which ? 100 : 200;
drawRotateRectangle(30, 30, rWidth, 30, 0.5);
which = !which;
}
render();
setInterval(render, 1000);
<canvas></canvas>
如您所见,当它变宽时,它也会向上移动。实际上,我希望矩形的左上角在调整大小前后位于同一点。
不过,我还希望它仍然绕着它的中心旋转。也就是说,我正在寻找在调整大小发生之前和之后必须将矩形移动多少(更改其 x 和 y 坐标),才能有效地将其锚定到同一位置。
这可能吗?如果是这样,您将如何计算必须修改矩形的 x
和 y
的值,以便在视觉上将其锁定到当前位置?
我知道如果我将矩形围绕其左上角而不是其中心旋转,则不会发生此问题,但出于我的应用目的,矩形 有 到围绕其中心旋转,因此我正在寻找我必须修改多少 x
和 y
组件才能保持其视觉位置。
因此,这个问题的答案不应涉及以任何方式修改 rotateCanvas
或 drawRotateRectangle
函数。
我正在寻找的效果示例:
沿旋转轴移动。旋转为 'r',距离为 'd'。假设变换是均匀的(即 x、y 比例在这种情况 1 上相同并且没有倾斜)
沿 x 轴移动 dx
并沿 y 轴移动 dy
的示例
var r = ?; // rotation in radians
var x = ?, y = ?; // in pixels. coordinate to move
var dx = ?; // distance in pixels along x axis
var dy = ?; // distance in pixels along y axis
const ax = Math.cos(r);
const ay = Math.sin(r);
x += dx * ax - dy * ay
y += dx * ay + dy * ax;
演示
展开一个矩形。函数rect.expand(left, right, top, bottom)
改变矩形的大小调整原点以保持原边不变。
const ctx = canvas.getContext("2d");
requestAnimationFrame(renderLoop);
const W = canvas.width, H = canvas.height;
var expanding = 1, expandCount = 0;
const rect = {
x: W / 2,
y: H / 2,
r: 0,
w: 100,
h: 50,
expand(left, right, top, bottom) {
const ax = Math.cos(this.r) * 0.5;
const ay = Math.sin(this.r) * 0.5;
this.w += left + right;
this.h += top + bottom;
this.x += -left * ax + right * ax + top * ay - bottom * ay;
this.y += -left * ay + right * ay - top * ax + bottom * ax;
},
draw() {
const ax = Math.cos(this.r);
const ay = Math.sin(this.r);
ctx.setTransform(ax, ay, -ay, ax, this.x, this.y);
ctx.strokeRect(-this.w * 0.5, -this.h * 0.5, this.w, this.h);
}
};
function renderLoop() {
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0, 0, W, H);
if (expanding === 1) {
expandCount += 1;
expandCount === 50 && (expanding = -1);
} else if (expanding === -1) {
expandCount -= 1;
expandCount === 0 && (expanding = 1);
}
rect.r += 0.01;
rect.expand(0, expanding, 0, 0);
rect.draw();
requestAnimationFrame(renderLoop);
}
canvas {
border: 1px solid black;
}
<canvas id = "canvas" width="200" height="200"></canvas>
其他答案很好很优雅,但你说你想保留 rotateCanvas
和 drawRotateRectangle
,对吧?
我们可以使用一些 matrix transformations on a DOMPoint
:
来模仿您的 rotateCanvas
函数中的转换
const x = 60;
const y = 30;
const h = 30;
const a = 0.5;
const tx = rWidth / 2;
const ty = h / 2;
// (0, 0) is the upper left of the rectangle
const pt = new DOMPoint(0, 0);
// Create transformation matrix based on center of rectangle
const mtr = new DOMMatrix()
.translateSelf(-tx, -ty)
// Important: rotateSelf takes degrees, not radians
.rotateSelf(a * 180 / Math.PI)
.translateSelf(tx, ty);
// Apply the transform
const tPt = pt.matrixTransform(mtr);
// Now the new call looks like this:
drawRotateRectangle(tPt.x + x, tPt.y + y, rWidth, h, a);
这是一个演示。我画了一个额外的红点来表明您可以使用 x
和 y
.
来控制要开始绘制矩形的位置
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext("2d");
function rotateCanvas(x, y, a) {
ctx.translate(x, y);
ctx.rotate(a);
ctx.translate(-x, -y);
}
function drawRotateRectangle(x, y, w, h, a) {
rotateCanvas(x + w / 2, y + h / 2, a);
ctx.fillRect(x, y, w, h);
rotateCanvas(x + w / 2, y + h / 2, -a);
}
let which = false;
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const rWidth = which ? 100 : 200;
const x = 30;
const y = 30;
const h = 30;
const a = Math.PI / 3;
const tx = rWidth / 2;
const ty = h / 2;
// Imitate the transform you use to calculate the offset point
const pt = new DOMPoint(0, 0);
const mtr = new DOMMatrix()
.translateSelf(-tx, -ty)
.rotateSelf(a * 180 / Math.PI)
.translateSelf(tx, ty);
const tPt = pt.matrixTransform(mtr);
drawRotateRectangle(tPt.x + x, tPt.y + y, rWidth, h, a);
// Just to show you can control the start point
ctx.fillStyle = 'red';
ctx.fillRect(x, y, 3, 3);
ctx.fillStyle = 'black';
which = !which;
}
render();
setInterval(render, 1000);
<canvas style="border: 1px solid black;"></canvas>
我有一个围绕其中心旋转的矩形,我正在尝试使其变宽。然而,因为它绕着它的中心旋转,使它变宽也在视觉上“移动”了矩形。
我正在寻找的是一种使其变宽然后相应地调整其 x 和 y 分量以使其在视觉上不移动的方法。我在这里创建了问题的重现,所以你可以看到问题:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
function rotateCanvas(x, y, a) {
ctx.translate(x, y);
ctx.rotate(a);
ctx.translate(-x, -y);
}
function drawRotateRectangle(x, y, w, h, a) {
rotateCanvas(x + w / 2, y + h / 2, a);
ctx.fillRect(x, y, w, h);
rotateCanvas(x + w / 2, y + h / 2, -a);
}
let which = true;
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const rWidth = which ? 100 : 200;
drawRotateRectangle(30, 30, rWidth, 30, 0.5);
which = !which;
}
render();
setInterval(render, 1000);
<canvas></canvas>
如您所见,当它变宽时,它也会向上移动。实际上,我希望矩形的左上角在调整大小前后位于同一点。
不过,我还希望它仍然绕着它的中心旋转。也就是说,我正在寻找在调整大小发生之前和之后必须将矩形移动多少(更改其 x 和 y 坐标),才能有效地将其锚定到同一位置。
这可能吗?如果是这样,您将如何计算必须修改矩形的 x
和 y
的值,以便在视觉上将其锁定到当前位置?
我知道如果我将矩形围绕其左上角而不是其中心旋转,则不会发生此问题,但出于我的应用目的,矩形 有 到围绕其中心旋转,因此我正在寻找我必须修改多少 x
和 y
组件才能保持其视觉位置。
因此,这个问题的答案不应涉及以任何方式修改 rotateCanvas
或 drawRotateRectangle
函数。
我正在寻找的效果示例:
沿旋转轴移动。旋转为 'r',距离为 'd'。假设变换是均匀的(即 x、y 比例在这种情况 1 上相同并且没有倾斜)
沿 x 轴移动 dx
并沿 y 轴移动 dy
的示例
var r = ?; // rotation in radians
var x = ?, y = ?; // in pixels. coordinate to move
var dx = ?; // distance in pixels along x axis
var dy = ?; // distance in pixels along y axis
const ax = Math.cos(r);
const ay = Math.sin(r);
x += dx * ax - dy * ay
y += dx * ay + dy * ax;
演示
展开一个矩形。函数rect.expand(left, right, top, bottom)
改变矩形的大小调整原点以保持原边不变。
const ctx = canvas.getContext("2d");
requestAnimationFrame(renderLoop);
const W = canvas.width, H = canvas.height;
var expanding = 1, expandCount = 0;
const rect = {
x: W / 2,
y: H / 2,
r: 0,
w: 100,
h: 50,
expand(left, right, top, bottom) {
const ax = Math.cos(this.r) * 0.5;
const ay = Math.sin(this.r) * 0.5;
this.w += left + right;
this.h += top + bottom;
this.x += -left * ax + right * ax + top * ay - bottom * ay;
this.y += -left * ay + right * ay - top * ax + bottom * ax;
},
draw() {
const ax = Math.cos(this.r);
const ay = Math.sin(this.r);
ctx.setTransform(ax, ay, -ay, ax, this.x, this.y);
ctx.strokeRect(-this.w * 0.5, -this.h * 0.5, this.w, this.h);
}
};
function renderLoop() {
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0, 0, W, H);
if (expanding === 1) {
expandCount += 1;
expandCount === 50 && (expanding = -1);
} else if (expanding === -1) {
expandCount -= 1;
expandCount === 0 && (expanding = 1);
}
rect.r += 0.01;
rect.expand(0, expanding, 0, 0);
rect.draw();
requestAnimationFrame(renderLoop);
}
canvas {
border: 1px solid black;
}
<canvas id = "canvas" width="200" height="200"></canvas>
其他答案很好很优雅,但你说你想保留 rotateCanvas
和 drawRotateRectangle
,对吧?
我们可以使用一些 matrix transformations on a DOMPoint
:
rotateCanvas
函数中的转换
const x = 60;
const y = 30;
const h = 30;
const a = 0.5;
const tx = rWidth / 2;
const ty = h / 2;
// (0, 0) is the upper left of the rectangle
const pt = new DOMPoint(0, 0);
// Create transformation matrix based on center of rectangle
const mtr = new DOMMatrix()
.translateSelf(-tx, -ty)
// Important: rotateSelf takes degrees, not radians
.rotateSelf(a * 180 / Math.PI)
.translateSelf(tx, ty);
// Apply the transform
const tPt = pt.matrixTransform(mtr);
// Now the new call looks like this:
drawRotateRectangle(tPt.x + x, tPt.y + y, rWidth, h, a);
这是一个演示。我画了一个额外的红点来表明您可以使用 x
和 y
.
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext("2d");
function rotateCanvas(x, y, a) {
ctx.translate(x, y);
ctx.rotate(a);
ctx.translate(-x, -y);
}
function drawRotateRectangle(x, y, w, h, a) {
rotateCanvas(x + w / 2, y + h / 2, a);
ctx.fillRect(x, y, w, h);
rotateCanvas(x + w / 2, y + h / 2, -a);
}
let which = false;
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const rWidth = which ? 100 : 200;
const x = 30;
const y = 30;
const h = 30;
const a = Math.PI / 3;
const tx = rWidth / 2;
const ty = h / 2;
// Imitate the transform you use to calculate the offset point
const pt = new DOMPoint(0, 0);
const mtr = new DOMMatrix()
.translateSelf(-tx, -ty)
.rotateSelf(a * 180 / Math.PI)
.translateSelf(tx, ty);
const tPt = pt.matrixTransform(mtr);
drawRotateRectangle(tPt.x + x, tPt.y + y, rWidth, h, a);
// Just to show you can control the start point
ctx.fillStyle = 'red';
ctx.fillRect(x, y, 3, 3);
ctx.fillStyle = 'black';
which = !which;
}
render();
setInterval(render, 1000);
<canvas style="border: 1px solid black;"></canvas>