HTML Canvas变换与旋转:正方形旋转中心偏移及通用计算方法
问题:Canvas正方形旋转未以中心为轴的原因及通用解决方法
问题描述
想要绘制一个正方形并将其旋转45deg,当前代码可运行,但旋转变换并未以正方形中心为轴(偏移约72px),请问为何会出现这种情况?如何通过编程计算任意尺寸正方形所需的变换参数?
现有代码
var canvas = document.querySelector('canvas'); canvas.width = 300; canvas.height = 400; let rotate = false; let clear = true; // if set to false then compare the two squares. let ctx = canvas.getContext('2d') setInterval(() => { ctx.fillStyle = 'white'; clear && ctx.fillRect(0,0,canvas.width,canvas.height); ctx.fillStyle = 'black'; ctx.save(); rotate = !rotate; if ( rotate ) { ctx.translate(150,128); // the center of the square is 150 / 200 ?? ctx.rotate(45 * Math.PI / 180); // 45 deg } else { ctx.translate(100,150); // top left of square } ctx.fillRect(0,0,100,100); ctx.restore(); },1000)
<canvas style="border: 1px solid red"></canvas>
问题原因
- 旋转中心坐标错误:未旋转状态下,正方形的左上角是
(100,150),尺寸为100x100,因此它的中心坐标应为100+100/2=150,150+100/2=200,即(150,200)。你当前旋转时使用的translate(150,128)并非正确的中心位置,直接导致旋转轴偏移。 - 变换与绘制的逻辑不匹配:Canvas的旋转变换是以当前原点为轴的。你在平移到目标点后,直接从
(0,0)绘制正方形,此时正方形的左上角对齐原点,旋转轴落在正方形的左上角而非中心,这是旋转后整体偏移的核心原因。
解决方法与通用参数计算
核心逻辑
要实现图形绕自身中心旋转,需遵循平移原点到图形中心 → 旋转 → 以原点为中心绘制图形的顺序:
- 计算目标正方形的中心坐标;
- 将Canvas原点平移到该中心;
- 执行旋转变换;
- 以原点为中心绘制正方形(即绘制的起始坐标为负的半宽、半高)。
任意尺寸正方形的参数计算
假设正方形的:
- 目标左上角坐标为
(x, y); - 宽度/高度为
size(正方形宽高相等);
则:
- 中心坐标:
centerX = x + size/2,centerY = y + size/2; - 绘制时的起始坐标:
(-size/2, -size/2)(确保正方形中心与原点重合);
修改后的代码
var canvas = document.querySelector('canvas'); canvas.width = 300; canvas.height = 400; let rotate = false; let clear = true; let ctx = canvas.getContext('2d') const squareX = 100; // 正方形左上角X const squareY = 150; // 正方形左上角Y const squareSize = 100; // 正方形尺寸 setInterval(() => { ctx.fillStyle = 'white'; clear && ctx.fillRect(0,0,canvas.width,canvas.height); ctx.fillStyle = 'black'; ctx.save(); rotate = !rotate; if (rotate) { // 计算中心坐标 const centerX = squareX + squareSize / 2; const centerY = squareY + squareSize / 2; // 平移到中心 ctx.translate(centerX, centerY); // 旋转45度 ctx.rotate(45 * Math.PI / 180); // 以原点为中心绘制正方形 ctx.fillRect(-squareSize/2, -squareSize/2, squareSize, squareSize); } else { // 正常绘制(左上角对齐目标点) ctx.translate(squareX, squareY); ctx.fillRect(0,0,squareSize,squareSize); } ctx.restore(); }, 1000)
内容的提问来源于stack exchange,提问作者user3094755
相关产品推荐
相关产品推荐

