前言
在实际业务开发中,经常会遇到上传图片时要添加上自己网站水印,这个水印各种类型都有,这里主要讲下我在开发中,客户的需要在上传图片时添加上当前录入表单的一些信息,方便后期直接查看图片追溯信息。先放下最终实现的上传成功后缩略图和预览图:
实现该功能主要有2种:原生的canvas实现和使用html2canvas库。当前项目是基于element-ui的一个vue项目,提供思路,代码仅供参考。
原生的canvas
其思路就是File对象–>img DOM元素–>转为canvas–>画水印图片–>画文本–>转为blob–>转为File对象(后端上传需要File对象)–>调用接口上传给服务器
1、获取File对象,并转为DOM元素
element的上传组件自带回调函数里可以获取到当前上传文件的file对象,里面的file.raw就是我们要的File对象,代码如下:
// file为element的上传组件自带回调函数里的一个文件对象
file2Img(file) {
return new Promise((resolve, reject) => {
let img = new Image()
img.src = URL.createObjectURL(file.raw)
img.addEventListener('load', () => resolve(img))
})
}
2、DOM元素转为canvas对象
img2Canvas(img) {
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
return canvas
},
3、canvas对象里画水印背景图
// y是画图片的y轴定位坐标
drawWatermark(ctx, y) {
return new Promise((resolve, reject) => {
const img = new Image()
img.src = require('@/assets/images/watermark-bg.png')
img.addEventListener('load', () => {
ctx.drawImage(img, 0, y)
console.log(img)
resolve()
})
})
},
4、canvas对象里画文本
drawText(ctx, y) {
// 设置填充字号和字体,样式
ctx.font = '16px 宋体 bold'
ctx.fillStyle = 'rgba(255, 255, 255)'
// 设置左对齐
ctx.textAlign = 'left'
ctx.fillText('昭通高速巧家收费站', 100, y + 40)
ctx.fillStyle = 'rgba(0, 0, 0)'
ctx.fillText('巡查内容:日巡查', 10, y + 86)
},
5、canvas对象转为blob对象
canvas2Blob(canvas) {
return new Promise((resolve, reject) => {
canvas.toBlob(blob => resolve(blob))
})
},
6、最后通过调用上面的方法在上传前处理下文件即可
// file为element的上传组件自带回调函数里的一个文件对象
async addWatermark(file) {
// 把上传的图片转为img dom对象
const img = await this.file2Img(file)
// 把图片画在canvas上,并返回canvas对象
const canvas = this.img2Canvas(img)
const ctx = canvas.getContext('2d')
// 高度是根据固定的水印图片高度计算
const y = canvas.height - 240
// canvas上画水印图片
await this.drawWatermark(ctx, y)
// canvas上画文本
this.drawText(ctx, y)
// 转为blob对象
const blob = await this.canvas2Blob(canvas)
// 转为后端要的file对象
const newFile = new File([blob], file.name, { type: file.raw.type })
return newFile
}
html2canvas库
使用该库思路较为简单,就是把上传的图片作为背景图(html2canvas不能画背景图,这里用img标签替换),把水印通过html+css方式写在背景图上,然后用html2canvas来把html转化为canvas对象,再把canvas对象转为blob对象,最后转为File对象传给后端。
// html和css省略,主要就是写了一个以上传图片为背景,加上水印样式的dom
import html2canvas from 'html2canvas'
async addWatermark(file) {
// 获取用img标签代替背景图的img DOM元素
const uploadImg = document.querySelector('#uploadImg')
// file为element的上传组件自带回调函数里的一个文件对象,file.raw是上传文件的File对象,img显示当前上传图片
uploadImg.src = URL.createObjectURL(file.raw)
// document.querySelector('#capture')获取需要转为canvas的dom节点
const canvas = await html2canvas(document.querySelector('#capture'))
// 转为blob对象
const blob = await this.canvas2Blob(canvas)
// 转为后端要的file对象
const newFile = new File([blob], file.name, { type: file.raw.type })
return newFile
}
注意:html2canvas库绘制的DOM必须是可见的,但是实际项目中,用于转为canvas的这个DOM用户是不可见的,简单的方式就是通过css的定位属性,把这个DOM片段定位出浏览器视图外面。
优缺点
从代码里可以看出,原生canvas步骤比较繁琐,且很死板,添加的水印背景图还需要固定大小,那么里面的文字如果是不定的,很容易溢出背景图。而用html2canvas库就没这个顾虑,毕竟写css样式很灵活。但是这个库体量还是较大的,压缩后还有46kb,而且有冗余DOM。所以建议简单水印可用原生canvas,相对复杂的再用这个库。