纯前端使用Canvas旋转图片并封装成通用工具方法

简介

在现代前端开发中,Canvas 是一个强大的 HTML5 元素,它允许我们使用 JavaScript 在网页上绘制图形。通过使用 Canvas,我们可以实现各种各样的图形效果和交互功能。本文将介绍如何使用 Canvas 绘制图片并实现旋转功能,并将其封装成通用的工具方法,方便其他开发者在项目中使用。

图片旋转示意:

主要思路

加载图片

Web 环境中表示图片的形式有多种:

  • base64 字符串
  • 图片 URL 链接:包括在线 URL 链接、浏览器缓存中的 Blob 链接等
  • <img> 元素
  • <cavnas> 元素

其中 base64 字符串 和图片 URL 链接 需要先加载为 Image 对象 ,才能进行绘图,加载过程为:

构造一个 Image 对象,指定其 src 为要加载的数据或图片 URL 链接,对象的 onload 事件触发则表示图片加载完成。

计算输出图片尺寸

按照图片旋转角度不同可以分为两种情况:

  1. 旋转角度为 90° 的倍数
  2. 旋转角度非 90° 的倍数

若按照 90° 倍数旋转,输出图片尺寸一定是和原图一致的,要么宽高相同,要么宽高相反。

若按照非 90°倍数旋转,要看原图是按照裁切模式(类似于css里的cover)还是包含模式(类似于css里的contain)处理,输出图片的尺寸和原图尺寸不相同。

Canvas绘图并旋转

Canvas 中实现图片旋转绘图的思路是:

  • 先将绘图坐标系移动到图片中心点

  • 按照旋转角度旋转坐标系,这里以顺时针旋转45°为例

  • 从新的坐标系的原点开始绘图

    这样绘制的图片不在画布中心,需要偏移绘图起始点

  • 从新的坐标系偏移起始点为图片尺寸的一半进行绘图

    可以看到,旋转角度不是 90°,绘图结果超出了原始图片尺寸,需要进行裁剪或者缩小。

    如果旋转角度为 90° 或 90° 的倍数,以 90° 为例

    先进行了前面计算输出图片尺寸的步骤,能保证输出图片不被裁剪。

    如果旋转角度为180°或180°的倍数,则旋转后图片尺寸与原图完全一样。

输出图片

前端输出图片有两种形式:输出图片数据 、输出本地预览链接,这里的预览链接不是那种 http 开头的 URL 地址。

对于上述实现方式来说,直接返回创建的 Canvas 对象就可以,通过 canvas.toBlob() 返回一个 Blob 对象,也可以使用 cavnas.toDataURL() 直接得到图片 base64 字符串。

详细实现步骤

下面介绍图片旋转功能的详细代码实现,完整代码已开源,可前往 GitHub 查看。

加载图片

封装成一个工具方法,根据传入的图片是否 base64 字符串、是否 URL 链接、是否是 Blob 链接来决定是否拼接图片基础路径。

ts 复制代码
// 这里省略了isUrlLike isBlobUrlLike
export function loadImage (
  url: string,
  /**
   * @defaultValue `false`
   */
  isBase64: boolean = false,
  /**
   * @defaultValue `''`
   */
  baseUrl: string = ''
): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    let tempUrl = isBase64 ? url : (isUrlLike(url) || isBlobUrlLike(url)) ? url : baseUrl + url
    const image = new Image()
    image.src = tempUrl
    image.crossOrigin = 'anonymous'
    image.onload = () => { resolve(image) }
    image.onerror = (e) => { reject(e) }
  })
}

计算输出图片尺寸

当旋转角度是 90° 或 270° 时,输出图片与原图尺寸互换;当旋转角度为 0° 或 360° 时,两者尺寸保持一致。

ts 复制代码
// 这是部分代码
if (degree === 90 || degree === 270) {
  canvas.width = image.height
  canvas.height = image.width
} else {
  canvas.width = image.width
  canvas.height = image.height
}

Canvas绘图

使用 translate 方法移动绘图坐标系原点到输出图片中心。

转换角度为弧度,使用 rotate 方法旋转绘图坐标系。

使用 drawImage 方法沿着新坐标系绘图,绘图起始点向 X 轴、Y 轴负方向偏移图片一半尺寸。

ts 复制代码
// 这是部分代码
const radians = degree * Math.PI / 180
ctx?.translate(canvas.width / 2, canvas.height / 2)
ctx?.rotate(radians)
ctx?.drawImage(image, -image.width / 2, -image.height / 2)
return canvas

输出图片

工具方法直接返回绘图的 Canvas 对象。

获取输出图片 base64 数据:

ts 复制代码
const rotatedImage = rotateImage(img, 90)
rotatedImage.toDataURL() // 返回base64数据

获取输出图片 Blob 对象:

ts 复制代码
const rotatedImage = rotateImage(img, 90)
rotatedImage.toBlob() // 返回Blob对象

获取图片本地预览链接:

ts 复制代码
// 这里使用@dinofe/xt-core/web工具库中Blob转URL的工具方法
import { convertBlobToUrl } from '@dinofe/xt-core/web'
const rotatedImage = rotateImage(img, 90)
convertBlobToUrl(rotatedImage.toBlob()) // Blob生成的本地预览连接

封装工具方法

为了方便复用和维护,我们可以将实现的旋转图片功能封装成一个方法,然后发不成 npm 包。

这一步需要考虑 npm 包各种模块规范的兼容性、JS 代码语法兼容性、polyfill 等,比较复杂,这里有比较完整实现可以参考。

总结

本文介绍了如何使用 Canvas 实现了纯前端进行图片旋转的功能,同时还将这一功能的实现封装成了一个工具方法方便复用。不过,文中的图片旋转实现只支持进行 90° 倍数的角度旋转,后面再考虑支持任意角度旋转。

参考资料

相关推荐
电子IT笔记几秒前
对接群晖NAS,上传和删除踩坑记录
前端·后端
计算机魔术师4 分钟前
半年前说不缺钱,半年后150亿美元超募
前端
Mk598785 分钟前
P4:把 Agent 的记忆搬进 Redis —— AgentScope-Java 工程化实战
后端
用户5708462574405 分钟前
自动化发布器里最贵的 Bug:返回成功,但什么都没发生
前端
雨中飘荡的记忆6 分钟前
Claude Code 入门必会:@、!、Slash Commands,一篇讲清楚
人工智能·后端
爱吃馒头不吃辣11 分钟前
加一道题,改一天代码:题库即数据的降维打击
前端·架构
aiks_yuan14 分钟前
Requestly规则使用技巧:前端联调、接口Mock与网络请求调试实战
前端
程序员老赵18 分钟前
Docker 部署 MaxKB:轻松搭建企业级智能体平台
前端·docker·agent
用户83562907805121 分钟前
如何使用 Python 在 Word 文档中创建和格式化列表
后端·python
开心就好202527 分钟前
uni-app 本地打包 iOS,在 Windows 上使用 KXBuild 产出 ipa 并上架的完整流程
后端·ios