简介
在现代前端开发中,Canvas 是一个强大的 HTML5 元素,它允许我们使用 JavaScript 在网页上绘制图形。通过使用 Canvas,我们可以实现各种各样的图形效果和交互功能。本文将介绍如何使用 Canvas 绘制图片并实现旋转功能,并将其封装成通用的工具方法,方便其他开发者在项目中使用。
图片旋转示意:
主要思路
加载图片
Web 环境中表示图片的形式有多种:
- base64 字符串
- 图片 URL 链接:包括在线 URL 链接、浏览器缓存中的 Blob 链接等
<img>
元素<cavnas>
元素
其中 base64 字符串 和图片 URL 链接 需要先加载为 Image
对象 ,才能进行绘图,加载过程为:
构造一个 Image
对象,指定其 src
为要加载的数据或图片 URL 链接,对象的 onload
事件触发则表示图片加载完成。
计算输出图片尺寸
按照图片旋转角度不同可以分为两种情况:
- 旋转角度为 90° 的倍数
- 旋转角度非 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° 倍数的角度旋转,后面再考虑支持任意角度旋转。