无任何外部依赖,纯javaScript实现图片添加水印,同时提供图片旋转任意角度的代码实现
# 一、效果展示
将文件拖到此处,或点击上传
加水印前
加水印后
# 二、源码实现
创建文件 watermark.js
/**
* 为图片添加水印
* @param url 原始图片url
* @param watermarkText 水印文案
* @param angle 水印旋转角度
* @param fontSize 水印字体大小
* @param textColor 水印颜色,支持rgba或者16进制颜色值
* @param mode 水印渲染模式,默认 interval 错行渲染。可选值 repeat 重复渲染
* @param tileSize 平铺块的大小
* @param padding 水印之间的间距
* @param rotateAngle 图片旋转角度 负数为逆时针旋转
* @param type 生成的图片类型,默认: 'image/png'
* @returns {Promise<unknown>}
*/
export function getWatermarkImg(
{
url,
watermarkText = '测试水印',
angle = -30, // 水印旋转角度,负数表示逆时针旋转
fontSize = 14, //水印文字的大小(以像素为单位)
textColor = "rgba(0, 0, 0, 0.4)",
mode = 'interval', // 可选值 'repeat' 重复展示
tileSize = 200, // 平铺块的大小
padding = 10, // 水印之间的间距
rotateAngle = 0, // 图片旋转角度,默认为0度,负数为逆时针旋转
quality = 1, // 图片品质,取值0-1,值越大,生成的图片越大
type = 'image/png', // 生成的图片类型,默认: 'image/png'
}
) {
return new Promise(async (resolve, reject) => {
// 如果图片需要旋转
if (rotateAngle !== 0) {
// 将图片旋转指定角度
try {
const value = await this.rotateImage({url, angle: rotateAngle})
url = value.base64
} catch (e) {
reject(e)
}
}
// 加载图片
const image = new Image();
image.src = url;
image.onerror = () => {
reject('获取图片失败')
}
image.onload = () => {
// 创建 Canvas 元素并获取上下文
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
// 设置 Canvas 的宽度和高度,需要和图片一致
canvas.width = image.width;
canvas.height = image.height;
// 将图片绘制到 Canvas 上
context.drawImage(image, 0, 0);
// 计算旋转后 Canvas 的中心点坐标
let centerX = canvas.width / 2;
let centerY = canvas.height / 2;
// 设置旋转变换矩阵
context.translate(centerX, centerY); // 先将原点移到 Canvas 中心
context.rotate(angle * Math.PI / 180); // 以 Canvas 中心为轴心进行旋转
context.translate(-centerX, -centerY); // 将原点移回到左上角
// 设置文字样式
context.font = fontSize + 'px Arial'; // 字体样式和大小
// 绘制重复水印
if (mode === 'repeat') {
// 循环绘制水印
for (let x = 0; x < canvas.width; x += tileSize + padding) {
for (let y = 0; y < canvas.height; y += tileSize + padding) {
context.fillStyle = textColor;
context.fillText(watermarkText, x, y); // 绘制水印文本
}
}
} else {
// 循环绘制错行水印
for (let x = 0; x < canvas.width; x += tileSize + padding) {
for (let y = 0; y < canvas.height; y += tileSize + padding) {
context.fillStyle = textColor;
// 根据所在行数计算垂直偏移量
let lineOffset = (x / (tileSize + padding)) % 2 === 0 ? 0 : (tileSize + padding) / 2;
// 根据所在行数判断是否错行绘制水印文本
if ((x / (tileSize + padding)) % 2 === 0) {
context.fillText(watermarkText, x, y + lineOffset); // 绘制水印文本
} else {
context.fillText(watermarkText, x, y - lineOffset + tileSize); // 绘制水印文本
}
}
}
}
try {
// 将带有水印的图片转换为 Base64 格式
const watermarkedImageSrc = canvas.toDataURL(type, quality);
resolve({
base64: watermarkedImageSrc
})
} catch (e) {
reject(e)
}
};
})
},
/**
* 旋转图片
* @param url 图片url
* @param angle 图片旋转的角度,负数为逆时针旋转
* @param type 生成的图片类型,默认: 'image/png'
* @returns {Promise<unknown>}
*/
export function rotateImage(
{
url,
angle = 0,
type = 'image/png'
}
) {
return new Promise((resolve, reject) => {
// 创建一个 Image 对象
const image = new Image();
image.src = url
// 设置当图片加载完成后的回调函数
image.onload = function () {
// 创建一个 Canvas 元素
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
// 计算旋转后的画布尺寸
const width = image.width;
const height = image.height;
const radians = angle * Math.PI / 180;
const rotatedWidth = Math.abs(width * Math.cos(radians)) + Math.abs(height * Math.sin(radians));
const rotatedHeight = Math.abs(width * Math.sin(radians)) + Math.abs(height * Math.cos(radians));
// 设置画布尺寸
canvas.width = rotatedWidth;
canvas.height = rotatedHeight;
// 平移到画布中心并旋转
context.translate(rotatedWidth / 2, rotatedHeight / 2);
context.rotate(radians);
// 绘制旋转后的图像
context.drawImage(image, -width / 2, -height / 2, width, height);
// 将旋转后的图像转换为base64
const rotatedImageSrc = canvas.toDataURL(type);
resolve({
base64: rotatedImageSrc
})
};
// 设置当图片加载失败后的回调函数
image.onerror = function () {
reject('无法加载图片');
};
});
}
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# 三、使用
import {getWatermarkImg} from 'watermark.js'
getWatermarkImg({
url: '',
// ...
}).then(res=>{
const { base64 } = res
console.log(base64)
})
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 四、可能会使用到的图片处理工具方法
/**
* 图片base64转file
* @param base64
* @param fileName
* @returns {File}
*/
export function base64ToFile(base64, fileName) {
let arr = base64.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([new Blob([u8arr], {type: mime})], fileName, {type: mime})
}
/**
* 图片base64转url
* @param base64
* @param fileName
* @returns {string}
*/
export function base64ToUrl(base64, fileName) {
const blob = base64ToFile(base64, fileName)
const blobUrl = URL.createObjectURL(blob)
return blobUrl
}
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
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