纯前端使用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° 倍数的角度旋转,后面再考虑支持任意角度旋转。

参考资料

相关推荐
HUMHSX3 分钟前
Vue 项目启动全流程解析:从入口文件到全局指令注册与页面渲染
前端·javascript·vue.js
有颜有货14 分钟前
PMC生产排产的4种算法,一次讲清
java·服务器·前端
小虎牙00716 分钟前
Android kotlin图片库Coil源码详解
android·前端
随风一样自由26 分钟前
【前端领域】前端开发核心应用场景与落地实践
前端·前端框架
an317421 小时前
弹窗数据流设计的两种高阶架构实践
前端·vue.js·架构
utmhikari1 小时前
【日常随笔】深入回答纯Vibe Coding写后端项目的几个问题
后端·ai编程·vibecoding
尚早立志1 小时前
Spring Boot 源码研读之ConfigurableEnvironment 环境准备
java·spring boot·后端
谢尔登1 小时前
【React】 状态管理方案
前端·react.js·前端框架
用户2136610035721 小时前
Vue商品详情与放大镜组件
前端·javascript
半个落月1 小时前
从Tapas小Demo理清localStorage、事件与this
前端·javascript