You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何围绕Canvas任意原点实现图像的平移、缩放与旋转?

问题:Canvas图像围绕任意原点旋转的实现方案

我正在开发一款图像编辑工具,目前已实现Canvas上图像的拖拽、平移、缩放和旋转功能,基于DOMMatrix API实现变换。当前功能可用,但仅支持围绕图像中心点进行变换。

如何实现围绕Canvas任意原点(例如以Canvas中心(0.5, 0.5)、左下角(0.0, 0.1)等位置)进行旋转操作?

我尝试过为图像添加平移效果来围绕指定原点旋转,但当图像较大且部分超出Canvas时,需要让图像围绕Canvas中心点旋转,同时当前围绕Canvas中心点旋转的Demo存在指针拖拽对齐偏差的问题。


Demo说明

快捷键

  • 按下"r"键:图像旋转5°
  • 按下"-"键:图像缩小0.1倍
  • 按下"="键:图像放大0.1倍

示例代码

JavaScript

let rotation = 0;
let scale = 1;
let x = 0;
let y = 0;
let startX = 0;
let startY = 0;
let lastX = 0;
let lastY = 0;
let pointerDown = false;

const canvas = document.querySelector("#canvas");
const ctx = canvas.getContext("2d");

const imgWidth = 480;
const imgHeight = 300;

function resizeCanvas() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
}

resizeCanvas();
window.addEventListener("resize", resizeCanvas);

const img = new Image();
img.crossOrigin = "anonymous";
img.src = "https://i.imgur.com/3q3kNGh.png";

function onPointerDown(event) {
  pointerDown = true;
  startX = (event.clientX - canvas.offsetLeft) / imgWidth;
  startY = (event.clientY - canvas.offsetTop) / imgHeight;
}

function onPointerMove(event) {
  if (!pointerDown) return;
  x = lastX + ((event.clientX - canvas.offsetLeft) / imgWidth - startX);
  y = lastY + ((event.clientY - canvas.offsetTop) / imgHeight - startY);
}

function onPointerUp() {
  pointerDown = false;
  lastX = x;
  lastY = y;
}

window.addEventListener("pointerdown", onPointerDown);
window.addEventListener("pointermove", onPointerMove);
window.addEventListener("pointerup", onPointerUp);

window.addEventListener("keydown", (event) => {
  const key = event.key.toLowerCase();
  switch (key) {
    case "r":
      rotation = (rotation + 5) % 360;
      break;
    case "-":
      scale = Math.max(0, scale - 0.1);
      break;
    case "=":
      scale = Math.min(2, scale + 0.1);
      break;
    default:
      break;
  }
});

function rotate(x, y, rotation) {
  const panXX = x * Math.cos((rotation * Math.PI) / 180);
  const panXY = y * Math.sin((rotation * Math.PI) / 180);
  const panYY = y * Math.cos((rotation * Math.PI) / 180);
  const panYX = x * Math.sin((rotation * Math.PI) / 180);
  const panX = panXX + panXY;
  const panY = panYY - panYX;
  return { x: panX, y: panY };
}

(function draw() {
  requestAnimationFrame(draw);

  const imgX = imgWidth * x;
  const imgY = imgHeight * y;

  const { x: tX, y: tY } = rotate(imgX, imgY, rotation);

  const matrix = new DOMMatrix()
    .translate(imgWidth / 2, imgHeight / 2)
    .rotate(rotation)
    .translate(imgWidth / -2, imgHeight / -2)
    .translate(tX, tY)
    .scale(scale);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  ctx.setTransform(matrix);

  ctx.drawImage(img, 0, 0, imgWidth, imgHeight);

  ctx.resetTransform();
})();

CSS

html,
body {
  margin: 0;
  padding: 0;
}
canvas {
  display: block;
}

HTML

<canvas id="canvas"></canvas>

内容的提问来源于stack exchange,提问作者James Craig

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 04:00:26