图片加水印
先来第一版,使用canvas填充文字的方式
/**
* 添加水印
* @param {file} 上传的图片文件
*/
async function addWaterMarker(file) {
// 先将文件转成img标签
let img = await blobToImg(file)
return new Promise((resolve, reject) => {
// 创建canvas画布
let canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
let ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
// 设置填充字号和字体,样式,这里设置字体大小根据canvas的宽度等比缩放,防止大图片生成的水印很小的问题
ctx.font = `${canvas.width * 0.05}px 宋体`
ctx.fillStyle = "red"
// 设置右对齐
ctx.textAlign = 'right'
// 在指定位置绘制文字
ctx.fillText('我是水印1', canvas.width - 100, canvas.height - 100)
ctx.fillText('我是水印2', canvas.width - 100, canvas.height - 50)
// 将canvas转成blob文件返回
canvas.toBlob(blob => resolve(blob))
})
}
/**
* blob转img标签
*/
function blobToImg(blob) {
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.addEventListener('load', () => {
let img = new Image()
img.src = reader.result
img.addEventListener('load', () => resolve(img))
})
reader.readAsDataURL(blob)
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
水印确实加上去了,如果水印只是简单布局的文字,这也可以用 但思前想后总觉得不优雅,加上水印内容比较复杂,还有图片,靠这种方式要实现还是够呛的 经过苦苦折腾
第二版搞出来了,水印内容通过html转成图片,然后把水印图片合成到上传的图片中
/**
* 添加水印
* @param {file} 上传的图片文件
* @param {el} 水印内容html
*/
async function addWaterMarker(file, el = '#markImg') {
// 先将文件转成img标签
let img = await blobToImg(file)
return new Promise(async (resolve, reject) => {
try {
// 创建canvas画布
let canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
let ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
// 创建水印图片canvas画布
let markCanvas = document.createElement('canvas')
// 创建水印图片canvas
const markImg = await createMarkImg(document.querySelector(el))
//让水印根据图片尺寸等比缩放,默认宽度设成1000
let zoom = canvas.width / 1000
let markCtx = markCanvas.getContext('2d')
// 缩放水印图片canvas
markCtx.scale(zoom, zoom)
// 将水印画布的宽高设置成缩放后的水印图片canvas的宽高
markCanvas.width = markImg.width
markCanvas.height = markImg.height
// 将水印图片canvas填充到水印画布中
markCtx.drawImage(markImg, 0, 0)
// 将水印画布canvas填充到canvas画布
ctx.drawImage(markCanvas, canvas.width - markCanvas.width, canvas.height - markCanvas.height, markCanvas.width, markCanvas.height)
canvas.toBlob(blob => resolve(blob))
} catch (error) {
reject(error)
}
})
}
/**
* blob转img标签
*/
function blobToImg(blob) {
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.addEventListener('load', () => {
let img = new Image()
img.src = reader.result
img.addEventListener('load', () => resolve(img))
})
reader.readAsDataURL(blob)
})
}
/**
* 创建水印canvas,需要安装html2canvas.js插件
*/
function createMarkImg(el) {
return new Promise(async (resolve, reject) => {
try {
const markImg = await html2canvas(el, {
allowTaint: false, //允许污染
useCORS: true,
backgroundColor: null//'transparent' //背景色
})
resolve(markImg)
} catch (error) {
reject(error)
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
写得虽然复杂了点,但是很好用,水印内容使用html+css先画好,然后直接合成到图片的指定位置就行了,省心很多 但人有时候就是爱折腾,头天写完的代码第二天就是觉得很别扭,还是觉得不够优雅,自我总结至少有以下两个问题
1、水印部分为了实现缩放使用了两层canvas,对性能有一定的耗损,不妥
2、上传的图片加了水印后体积大了有两三倍,这还是在上传图片之前先进行压缩的前提下,这无疑会大大增加上传图片的速度
再次折腾,暂定的终极版终于出炉,代码如下
/**
* 添加水印
*/
export async function addWaterMarker(file, el = '#markImg') {
// 将文件blob转换成图片
let img = await blobToImg(file)
return new Promise(async (resolve, reject) => {
try {
// 创建canvas画布
let canvas = document.createElement('canvas')
//等比例调整canvas宽高,以缩小图片体积
let imgRatio = img.naturalWidth / img.naturalHeight //图片比例
canvas.width = 750 //默认设置成750
canvas.height = canvas.width / imgRatio
let ctx = canvas.getContext('2d')
// 填充上传的图片
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
// 生成水印图片
const markWidth = document.querySelector(el).clientWidth
let zoom = canvas.width * 0.3 / markWidth
let markEle = document.querySelector(el)
// 先缩放水印html再转成图片
markEle.style.transform = `scale(${zoom})`
const markImg = await htmlToCanvas(markEle)
// 填充水印
ctx.drawImage(markImg, canvas.width - markImg.width - 15 * zoom, canvas.height - markImg.height - 15 * zoom, markImg.width, markImg.height)
// 将canvas转换成blob
canvas.toBlob(blob => resolve(blob))
} catch (error) {
reject(error)
}
})
}
/**
* blob转img标签
*/
function blobToImg(blob) {
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.addEventListener('load', () => {
let img = new Image()
img.src = reader.result
img.addEventListener('load', () => resolve(img))
})
reader.readAsDataURL(blob)
})
}
/**
* html转成canvas,需要安装html2canvas.js插件
*/
export function htmlToCanvas(el, backgroundColor = 'rgba(0,0,0,.1)') {
return new Promise(async (resolve, reject) => {
try {
const markImg = await html2canvas(el, {
allowTaint: false, //允许污染
useCORS: true,
backgroundColor //'transparent' //背景色
})
resolve(markImg)
} catch (error) {
reject(error)
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
最近更新时间: 2023/07/17 13:56:35
- 01
- 2023/07/17 13:57:35
- 02
- 2023/07/17 10:12:59
- 03
- 2023/07/17 09:24:52