在 Web App 或网页设计中,有时会有需要旋转页面中的图片的需求。根据图片所应用地方的不同,我们可以有不同的方法来旋转图片。如在 canvas 中的图片,我们就需要借助 canvas 的渲染上下文中的方法来旋转图片;而如果图片只是页面中的一个 <img> 元素,我们则可以使用 CSS 样式来旋转图片。

在 Canvas 上绘制旋转过的图像

在 canvas 上,我们可以借助 drawImage() 方法来绘制图像;如果要绘制旋转过的图像,则在调用 drawImage() 方法前,需先对 canvas 的渲染上下文进行变换,之后再在变换后的渲染上下文中调用 drawImage() 来画图。

绘制以 90 度角为基数进行旋转的图像

如我们只需要对图片进行以 90 度角为基数的旋转,可以使用如下代码。该函数接受两个参数,其一是一张图片,图片可以是 HTMLImageElement, HTMLCanvasElementCanvasImageSource,其二是旋转的角度,以度为单位。最终返回经过旋转的图片,类型为 HTMLCanvasElement,可供 drawImage() 方法使用。

function rotateImage(image, angle) {
  // 创建临时画布
  var canvas = document.createElement('canvas');
  var context = canvas.getContext('2d');

  // 根据图片的宽高设定画布的宽高
  switch (angle) {
    case 0:
    case 180:
      canvas.width = image.width;
      canvas.height = image.height;
      break;
    case 90:
    case 270:
      canvas.width = image.height;
      canvas.height = image.width;
      break;
  }

  // 旋转画布并画图
  context.rotate(angle * Math.PI / 180);
  switch (angle) {
    case 0:
      context.drawImage(image, 0, 0);
      break;
    case 90:
      context.drawImage(image, 0, -image.height);
      break;
    case 180:
      context.drawImage(image, -image.width, -image.height);
      break;
    case 270:
      context.drawImage(image, -image.width, 0);
      break;
  }

  // 返回旋转后的图像 (HTMLCanvasElement) 供 `drawImage()` 使用
  return canvas;
}

其中,旋转画布并画图部分的代码的思路是:只对画布进行旋转,随后在旋转过后的坐标系中绘图时,通过设置 drawImage() 方法的绘图起始点的位置,来保证绘制的图像能够落在旋转前的坐标系的第一象限(x, y 正半轴包裹的区域),也即画布在页面上的可视区域。

这部分代码,也可以简化为对画布进行平移并旋转,使变换后的原点落在将要画的图像的中点上,以简化 drawImage() 部分的逻辑,如下所示:

  // 旋转画布并画图
  context.translate(canvas.width / 2, canvas.height / 2);
  context.rotate(angle * Math.PI / 180);
  context.drawImage(image, -image.width / 2, -image.height / 2);

参考: html - HTML5 Canvas Rotate Image - Stack Overflow

此外,我们可以看到,简化后的代码没有了对角度的判断,同时适用于任意角度的旋转。


原图 在 canvas 上旋转后的图

绘制以任意角度进行旋转的图像

在上文中,已经介绍了在临时画布上对图像进行任意角度旋转的方法。剩下的就是将上文代码中计算临时画布宽高的代码变得与具体所旋转的角度无关。

若要使计算画布的宽高与具体旋转的角度无关,其实就是要找出图像在旋转过程中,能够包围所有旋转了的图像的矩形的尺寸。该矩形的宽和高可以由以下公式给出:

\[rect.width = rect.height = max(img.width, img.height) \cdot \sqrt{2}\]

从而,我们可以将上面的只适用于以 90 度角为基数进行旋转的函数改写为如下适用于任意角度旋转的函数:

function rotateImage(image, angle) {
  // 创建临时画布
  var canvas = document.createElement('canvas');
  var context = canvas.getContext('2d');

  // 根据包围图片旋转形成的包络线的矩形的宽高设定画布的宽高
  canvas.width = canvas.height = Math.max(image.width, image.height) * Math.sqrt(2);

  // 旋转画布并画图
  context.translate(canvas.width / 2, canvas.height / 2);
  context.rotate(angle * Math.PI / 180);
  context.drawImage(image, -image.width / 2, -image.height / 2);

  // 返回旋转后的图像 (HTMLCanvasElement) 供 `drawImage()` 使用
  return canvas;
}

使用 CSS 样式旋转图像

如果只是想要旋转 HTML 页面中的一幅图像,而不是需要在 canvas 上绘制旋转过的图像的话,则只需要对图片应用 CSS 样式即可。

如:

img {
  transform: rotate(90deg);
}

上述例子中的 90deg 也可以换为其他角度如 -60deg, 123.4deg, 390deg 等,也可以使用其他单位如 3.14rad 等。


原图 使用 CSS 旋转后的图
原图 使用 CSS 旋转后的图

其中,

  • 关于 transform 的用法可以参考 MDN
  • transform 下各种变换的原点由 tansform-origin 给出;
  • 旋转角度的单位有 deg, grad, radturn,可以参考MDN上的详细说明;
  • 关于 CSS transform 的浏览器兼容性可以参考Can I use …