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

参考资料

相关推荐
枫叶_v20 分钟前
【SpringBoot】20 同步调用、异步调用、异步回调
java·spring boot·后端
神夜大侠38 分钟前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱41 分钟前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号1 小时前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy72931 小时前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html
前端郭德纲1 小时前
ES6的Iterator 和 for...of 循环
前端·ecmascript·es6
王解2 小时前
【模块化大作战】Webpack如何搞定CommonJS与ES6混战(3)
前端·webpack·es6
欲游山河十万里2 小时前
(02)ES6教程——Map、Set、Reflect、Proxy、字符串、数值、对象、数组、函数
前端·ecmascript·es6
明辉光焱2 小时前
【ES6】ES6中,如何实现桥接模式?
前端·javascript·es6·桥接模式
源码12152 小时前
ASP.NET MVC宠物商城系统
后端·asp.net·宠物