@hzab/utils 1.0.13 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +4 -0
- package/package.json +1 -1
- package/src/img-utils.ts +456 -340
package/changelog.md
CHANGED
package/package.json
CHANGED
package/src/img-utils.ts
CHANGED
|
@@ -1,340 +1,456 @@
|
|
|
1
|
-
export class IMAGE_TYPES {
|
|
2
|
-
static JPEG = "image/jpeg";
|
|
3
|
-
static PNG = "image/png";
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 检测空图片
|
|
8
|
-
* @param img
|
|
9
|
-
* @returns
|
|
10
|
-
*/
|
|
11
|
-
export function checkEmptyImg(img: HTMLImageElement) {
|
|
12
|
-
// 创建一个 Canvas 元素
|
|
13
|
-
const canvas = document.createElement("canvas");
|
|
14
|
-
const ctx = canvas.getContext("2d");
|
|
15
|
-
if (!ctx) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// 设置 Canvas 的宽度和高度与图片一致
|
|
20
|
-
canvas.width = img.width;
|
|
21
|
-
canvas.height = img.height;
|
|
22
|
-
// 将图片绘制到Canvas上
|
|
23
|
-
ctx.drawImage(img, 0, 0);
|
|
24
|
-
// 获取Canvas中的像素数据
|
|
25
|
-
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
26
|
-
const data = imageData.data;
|
|
27
|
-
|
|
28
|
-
// 判断从左上角到右下角的像素是否都是黑色
|
|
29
|
-
let topLeftToBottomRight = true;
|
|
30
|
-
|
|
31
|
-
const iUp = 0;
|
|
32
|
-
const redUp = data[iUp];
|
|
33
|
-
const greenUp = data[iUp + 1];
|
|
34
|
-
const blueUp = data[iUp + 2];
|
|
35
|
-
const alphaUp = data[iUp + 3];
|
|
36
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
37
|
-
const red = data[i];
|
|
38
|
-
const green = data[i + 1];
|
|
39
|
-
const blue = data[i + 2];
|
|
40
|
-
const alpha = data[i + 3];
|
|
41
|
-
|
|
42
|
-
if (red !== redUp || green !== greenUp || blue !== blueUp || alpha !== alphaUp) {
|
|
43
|
-
topLeftToBottomRight = false;
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// 判断从右上角到左下角的像素是否都是黑色
|
|
49
|
-
let topRightToBottomLeft = true;
|
|
50
|
-
const jDown = data.length - 4;
|
|
51
|
-
const redDown = data[jDown];
|
|
52
|
-
const greenDown = data[jDown + 1];
|
|
53
|
-
const blueDown = data[jDown + 2];
|
|
54
|
-
const alphaDown = data[jDown + 3];
|
|
55
|
-
|
|
56
|
-
for (let j = jDown; j >= 0; j -= 4) {
|
|
57
|
-
const red = data[j];
|
|
58
|
-
const green = data[j + 1];
|
|
59
|
-
const blue = data[j + 2];
|
|
60
|
-
const alpha = data[j + 3];
|
|
61
|
-
|
|
62
|
-
if (red !== redDown || green !== greenDown || blue !== blueDown || alpha !== alphaDown) {
|
|
63
|
-
topRightToBottomLeft = false;
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// 打印结果
|
|
69
|
-
if (topLeftToBottomRight && topRightToBottomLeft) {
|
|
70
|
-
return false;
|
|
71
|
-
} else {
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* 获取dom元素宽高
|
|
78
|
-
* @param d dom元素
|
|
79
|
-
* @returns {{width: number, height: number}}
|
|
80
|
-
*/
|
|
81
|
-
export function getDomWH(d: HTMLElement | HTMLImageElement | HTMLCanvasElement) {
|
|
82
|
-
if (d instanceof Image || d instanceof HTMLCanvasElement) {
|
|
83
|
-
return {
|
|
84
|
-
width: d.width || d.offsetWidth,
|
|
85
|
-
height: d.width || d.offsetHeight,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
width: d.offsetWidth,
|
|
90
|
-
height: d.offsetHeight,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* 设置dom元素宽高
|
|
96
|
-
* @param d dom元素
|
|
97
|
-
* @param w 宽
|
|
98
|
-
* @param h 高
|
|
99
|
-
*/
|
|
100
|
-
export function setDomWH(dom: HTMLElement, w: number, h: number) {
|
|
101
|
-
dom.setAttribute("width", `${w}`);
|
|
102
|
-
dom.setAttribute("height", `${h}`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* 获取当前 canvas 图片 Blob 对象
|
|
107
|
-
* @param canvas
|
|
108
|
-
* @returns {Promise<Blob>}
|
|
109
|
-
*/
|
|
110
|
-
export function getCanvasPic(canvas: HTMLCanvasElement): Blob {
|
|
111
|
-
var gl = canvas.getContext("2d", { preserveDrawingBuffer: true });
|
|
112
|
-
|
|
113
|
-
return dataURItoBlob(canvas.toDataURL(IMAGE_TYPES.PNG, 1));
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* base64 字符串转 blob
|
|
118
|
-
* @param base64
|
|
119
|
-
* @returns
|
|
120
|
-
*/
|
|
121
|
-
export function dataURItoBlob(base64: string): Blob {
|
|
122
|
-
let arr = base64.split(",");
|
|
123
|
-
let mime = arr[0]?.match(/:(.*?);/)?.[1];
|
|
124
|
-
let bstr = atob(arr[1]);
|
|
125
|
-
let n = bstr.length;
|
|
126
|
-
let u8arr = new Uint8Array(n);
|
|
127
|
-
while (n--) {
|
|
128
|
-
u8arr[n] = bstr.charCodeAt(n);
|
|
129
|
-
}
|
|
130
|
-
return new Blob([u8arr], { type: mime });
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* 获取图片元素
|
|
135
|
-
* @param imgSrc
|
|
136
|
-
* @returns
|
|
137
|
-
*/
|
|
138
|
-
export function getImgEle(imgSrc: string): Promise<HTMLImageElement> {
|
|
139
|
-
return new Promise((resolve) => {
|
|
140
|
-
const img = new Image();
|
|
141
|
-
img.onload = function () {
|
|
142
|
-
resolve(img);
|
|
143
|
-
};
|
|
144
|
-
img.src = imgSrc;
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* 获取图片 Base64 字符串
|
|
150
|
-
* @param imgSrc
|
|
151
|
-
* @returns
|
|
152
|
-
*/
|
|
153
|
-
export function getImgBase64(imgSrc: string): Promise<string> {
|
|
154
|
-
return getImgEle(imgSrc).then((img) => {
|
|
155
|
-
const canvas = document.createElement("canvas");
|
|
156
|
-
let { width, height } = getDomWH(img);
|
|
157
|
-
setDomWH(canvas, width, height);
|
|
158
|
-
const ctx = canvas.getContext("2d");
|
|
159
|
-
ctx && ctx.drawImage(img, 0, 0, width, height, 0, 0, width, height);
|
|
160
|
-
return canvas.toDataURL(IMAGE_TYPES.PNG, 1);
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* 通过图片地址获取对应 canvas
|
|
166
|
-
* @param imgSrc
|
|
167
|
-
* @returns
|
|
168
|
-
*/
|
|
169
|
-
export function getCanvas(imgSrc: string): Promise<HTMLCanvasElement> {
|
|
170
|
-
return getImgEle(imgSrc).then((img) => {
|
|
171
|
-
const canvas = document.createElement("canvas");
|
|
172
|
-
let { width, height } = getDomWH(img);
|
|
173
|
-
setDomWH(canvas, width, height);
|
|
174
|
-
const ctx = canvas.getContext("2d");
|
|
175
|
-
ctx && ctx.drawImage(img, 0, 0, width, height, 0, 0, width, height);
|
|
176
|
-
return canvas;
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* 通过图片地址获取对应 canvas
|
|
182
|
-
* @param img
|
|
183
|
-
* @returns
|
|
184
|
-
*/
|
|
185
|
-
export function getCanvasByImg(img: HTMLImageElement): HTMLCanvasElement {
|
|
186
|
-
const canvas = document.createElement("canvas");
|
|
187
|
-
let { width, height } = getDomWH(img);
|
|
188
|
-
setDomWH(canvas, width, height);
|
|
189
|
-
const ctx = canvas.getContext("2d");
|
|
190
|
-
ctx && ctx.drawImage(img, 0, 0, width, height, 0, 0, width, height);
|
|
191
|
-
return canvas;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* 截取图片
|
|
196
|
-
* @param img
|
|
197
|
-
* @param opt
|
|
198
|
-
* @param opt.startX
|
|
199
|
-
* @param opt.startY
|
|
200
|
-
* @param opt.width
|
|
201
|
-
* @param opt.height
|
|
202
|
-
* @returns Promise<string>
|
|
203
|
-
*/
|
|
204
|
-
export function cropImage(
|
|
205
|
-
img: HTMLImageElement | HTMLCanvasElement,
|
|
206
|
-
opt: { startX: number; startY: number; width: number; height: number },
|
|
207
|
-
) {
|
|
208
|
-
return cropImageToCanvas(img, opt).toDataURL(IMAGE_TYPES.PNG, 1);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* 截取图片
|
|
213
|
-
* @param img
|
|
214
|
-
* @param opt
|
|
215
|
-
* @param opt.startX
|
|
216
|
-
* @param opt.startY
|
|
217
|
-
* @param opt.width
|
|
218
|
-
* @param opt.height
|
|
219
|
-
* @returns Promise<HTMLCanvasElement>
|
|
220
|
-
*/
|
|
221
|
-
export function cropImageToCanvas(
|
|
222
|
-
img: HTMLImageElement | HTMLCanvasElement,
|
|
223
|
-
opt: { startX: number; startY: number; width: number; height: number },
|
|
224
|
-
) {
|
|
225
|
-
const { startX, startY, width, height } = opt;
|
|
226
|
-
const canvas = document.createElement("canvas");
|
|
227
|
-
const ctx = canvas.getContext("2d");
|
|
228
|
-
// 设置 canvas 的宽度和高度为裁剪后的尺寸
|
|
229
|
-
canvas.width = width; // 裁剪后的宽度
|
|
230
|
-
canvas.height = height; // 裁剪后的高度
|
|
231
|
-
// 在 canvas 上绘制裁剪后的图像
|
|
232
|
-
ctx?.drawImage(img, startX, startY, width, height, 0, 0, width, height);
|
|
233
|
-
// 可以将裁剪后的图像数据获取出来
|
|
234
|
-
return canvas;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* 绘制图片
|
|
239
|
-
* @param img
|
|
240
|
-
* @param opt
|
|
241
|
-
* @param opt.startX
|
|
242
|
-
* @param opt.startY
|
|
243
|
-
* @param opt.width
|
|
244
|
-
* @param opt.height
|
|
245
|
-
* @returns Promise<string>
|
|
246
|
-
*/
|
|
247
|
-
export function imgToCanvas(img: HTMLImageElement) {
|
|
248
|
-
const canvas = document.createElement("canvas");
|
|
249
|
-
const ctx = canvas.getContext("2d");
|
|
250
|
-
canvas.width = img.width;
|
|
251
|
-
canvas.height = img.height;
|
|
252
|
-
ctx?.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
|
|
253
|
-
return canvas;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* 缩放图片
|
|
258
|
-
* @param img
|
|
259
|
-
* @param opt
|
|
260
|
-
* @returns base64
|
|
261
|
-
*/
|
|
262
|
-
export function scaleImage(img: HTMLImageElement | HTMLCanvasElement, opt = { scaleFactor: 1 }) {
|
|
263
|
-
return scaleImageToCanvas(img, opt).toDataURL(IMAGE_TYPES.PNG, 1);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* 缩放图片,返回 canvas
|
|
268
|
-
* @param img
|
|
269
|
-
* @param opt
|
|
270
|
-
* @returns canvas
|
|
271
|
-
*/
|
|
272
|
-
export function scaleImageToCanvas(img: HTMLImageElement | HTMLCanvasElement, opt = { scaleFactor: 1 }) {
|
|
273
|
-
const { scaleFactor = 1 } = opt;
|
|
274
|
-
const canvas = document.createElement("canvas");
|
|
275
|
-
const ctx = canvas.getContext("2d");
|
|
276
|
-
canvas.width = img.width * scaleFactor;
|
|
277
|
-
canvas.height = img.height * scaleFactor;
|
|
278
|
-
ctx?.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
|
|
279
|
-
return canvas;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* 图片转为 base64
|
|
284
|
-
* @param img
|
|
285
|
-
* @returns
|
|
286
|
-
*/
|
|
287
|
-
export async function imgToBase64(img: string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement) {
|
|
288
|
-
if (!img || img instanceof Blob) {
|
|
289
|
-
return img;
|
|
290
|
-
}
|
|
291
|
-
let _img = img;
|
|
292
|
-
if (img instanceof Image) {
|
|
293
|
-
_img = img.src;
|
|
294
|
-
} else if (img instanceof HTMLVideoElement) {
|
|
295
|
-
// 从视频中获取图片
|
|
296
|
-
const canvas = document.createElement("canvas");
|
|
297
|
-
let { width, height } = getDomWH(img);
|
|
298
|
-
setDomWH(canvas, width, height);
|
|
299
|
-
const ctx = canvas.getContext("2d");
|
|
300
|
-
ctx && ctx.drawImage(img, 0, 0, width, height, 0, 0, width, height);
|
|
301
|
-
_img = canvas.toDataURL(IMAGE_TYPES.PNG, 1);
|
|
302
|
-
} else if (img instanceof HTMLCanvasElement) {
|
|
303
|
-
_img = img.toDataURL(IMAGE_TYPES.PNG, 1);
|
|
304
|
-
} else if (typeof _img === "string" && !_img.startsWith("data:image/")) {
|
|
305
|
-
// 加载图片,并获取图片 base64
|
|
306
|
-
_img = await getImgBase64(_img);
|
|
307
|
-
}
|
|
308
|
-
return _img;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* 图片转为 Blob
|
|
313
|
-
* @param img
|
|
314
|
-
* @returns
|
|
315
|
-
*/
|
|
316
|
-
export async function imgToBlob(img: string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement): Promise<Blob> {
|
|
317
|
-
if (!img) {
|
|
318
|
-
throw new Error("请传入图片数据");
|
|
319
|
-
}
|
|
320
|
-
if (img instanceof Blob) {
|
|
321
|
-
return Promise.resolve(img);
|
|
322
|
-
}
|
|
323
|
-
const _img = await imgToBase64(img);
|
|
324
|
-
if (typeof _img !== "string") {
|
|
325
|
-
throw new Error("传入的图片数据不正确");
|
|
326
|
-
}
|
|
327
|
-
return dataURItoBlob(_img);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* 获取图片的 base64、blob 格式
|
|
332
|
-
* @param img
|
|
333
|
-
*/
|
|
334
|
-
export async function getAllTypeImg(img: string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement) {
|
|
335
|
-
const base64 = await imgToBase64(img);
|
|
336
|
-
return {
|
|
337
|
-
base64,
|
|
338
|
-
blob: typeof base64 === "string" && dataURItoBlob(base64),
|
|
339
|
-
};
|
|
340
|
-
}
|
|
1
|
+
export class IMAGE_TYPES {
|
|
2
|
+
static JPEG = "image/jpeg";
|
|
3
|
+
static PNG = "image/png";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 检测空图片
|
|
8
|
+
* @param img
|
|
9
|
+
* @returns
|
|
10
|
+
*/
|
|
11
|
+
export function checkEmptyImg(img: HTMLImageElement) {
|
|
12
|
+
// 创建一个 Canvas 元素
|
|
13
|
+
const canvas = document.createElement("canvas");
|
|
14
|
+
const ctx = canvas.getContext("2d");
|
|
15
|
+
if (!ctx) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 设置 Canvas 的宽度和高度与图片一致
|
|
20
|
+
canvas.width = img.width;
|
|
21
|
+
canvas.height = img.height;
|
|
22
|
+
// 将图片绘制到Canvas上
|
|
23
|
+
ctx.drawImage(img, 0, 0);
|
|
24
|
+
// 获取Canvas中的像素数据
|
|
25
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
26
|
+
const data = imageData.data;
|
|
27
|
+
|
|
28
|
+
// 判断从左上角到右下角的像素是否都是黑色
|
|
29
|
+
let topLeftToBottomRight = true;
|
|
30
|
+
|
|
31
|
+
const iUp = 0;
|
|
32
|
+
const redUp = data[iUp];
|
|
33
|
+
const greenUp = data[iUp + 1];
|
|
34
|
+
const blueUp = data[iUp + 2];
|
|
35
|
+
const alphaUp = data[iUp + 3];
|
|
36
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
37
|
+
const red = data[i];
|
|
38
|
+
const green = data[i + 1];
|
|
39
|
+
const blue = data[i + 2];
|
|
40
|
+
const alpha = data[i + 3];
|
|
41
|
+
|
|
42
|
+
if (red !== redUp || green !== greenUp || blue !== blueUp || alpha !== alphaUp) {
|
|
43
|
+
topLeftToBottomRight = false;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 判断从右上角到左下角的像素是否都是黑色
|
|
49
|
+
let topRightToBottomLeft = true;
|
|
50
|
+
const jDown = data.length - 4;
|
|
51
|
+
const redDown = data[jDown];
|
|
52
|
+
const greenDown = data[jDown + 1];
|
|
53
|
+
const blueDown = data[jDown + 2];
|
|
54
|
+
const alphaDown = data[jDown + 3];
|
|
55
|
+
|
|
56
|
+
for (let j = jDown; j >= 0; j -= 4) {
|
|
57
|
+
const red = data[j];
|
|
58
|
+
const green = data[j + 1];
|
|
59
|
+
const blue = data[j + 2];
|
|
60
|
+
const alpha = data[j + 3];
|
|
61
|
+
|
|
62
|
+
if (red !== redDown || green !== greenDown || blue !== blueDown || alpha !== alphaDown) {
|
|
63
|
+
topRightToBottomLeft = false;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 打印结果
|
|
69
|
+
if (topLeftToBottomRight && topRightToBottomLeft) {
|
|
70
|
+
return false;
|
|
71
|
+
} else {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 获取dom元素宽高
|
|
78
|
+
* @param d dom元素
|
|
79
|
+
* @returns {{width: number, height: number}}
|
|
80
|
+
*/
|
|
81
|
+
export function getDomWH(d: HTMLElement | HTMLImageElement | HTMLCanvasElement) {
|
|
82
|
+
if (d instanceof Image || d instanceof HTMLCanvasElement) {
|
|
83
|
+
return {
|
|
84
|
+
width: d.width || d.offsetWidth,
|
|
85
|
+
height: d.width || d.offsetHeight,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
width: d.offsetWidth,
|
|
90
|
+
height: d.offsetHeight,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 设置dom元素宽高
|
|
96
|
+
* @param d dom元素
|
|
97
|
+
* @param w 宽
|
|
98
|
+
* @param h 高
|
|
99
|
+
*/
|
|
100
|
+
export function setDomWH(dom: HTMLElement, w: number, h: number) {
|
|
101
|
+
dom.setAttribute("width", `${w}`);
|
|
102
|
+
dom.setAttribute("height", `${h}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 获取当前 canvas 图片 Blob 对象
|
|
107
|
+
* @param canvas
|
|
108
|
+
* @returns {Promise<Blob>}
|
|
109
|
+
*/
|
|
110
|
+
export function getCanvasPic(canvas: HTMLCanvasElement): Blob {
|
|
111
|
+
var gl = canvas.getContext("2d", { preserveDrawingBuffer: true });
|
|
112
|
+
|
|
113
|
+
return dataURItoBlob(canvas.toDataURL(IMAGE_TYPES.PNG, 1));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* base64 字符串转 blob
|
|
118
|
+
* @param base64
|
|
119
|
+
* @returns
|
|
120
|
+
*/
|
|
121
|
+
export function dataURItoBlob(base64: string): Blob {
|
|
122
|
+
let arr = base64.split(",");
|
|
123
|
+
let mime = arr[0]?.match(/:(.*?);/)?.[1];
|
|
124
|
+
let bstr = atob(arr[1]);
|
|
125
|
+
let n = bstr.length;
|
|
126
|
+
let u8arr = new Uint8Array(n);
|
|
127
|
+
while (n--) {
|
|
128
|
+
u8arr[n] = bstr.charCodeAt(n);
|
|
129
|
+
}
|
|
130
|
+
return new Blob([u8arr], { type: mime });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 获取图片元素
|
|
135
|
+
* @param imgSrc
|
|
136
|
+
* @returns
|
|
137
|
+
*/
|
|
138
|
+
export function getImgEle(imgSrc: string): Promise<HTMLImageElement> {
|
|
139
|
+
return new Promise((resolve) => {
|
|
140
|
+
const img = new Image();
|
|
141
|
+
img.onload = function () {
|
|
142
|
+
resolve(img);
|
|
143
|
+
};
|
|
144
|
+
img.src = imgSrc;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 获取图片 Base64 字符串
|
|
150
|
+
* @param imgSrc
|
|
151
|
+
* @returns
|
|
152
|
+
*/
|
|
153
|
+
export function getImgBase64(imgSrc: string): Promise<string> {
|
|
154
|
+
return getImgEle(imgSrc).then((img) => {
|
|
155
|
+
const canvas = document.createElement("canvas");
|
|
156
|
+
let { width, height } = getDomWH(img);
|
|
157
|
+
setDomWH(canvas, width, height);
|
|
158
|
+
const ctx = canvas.getContext("2d");
|
|
159
|
+
ctx && ctx.drawImage(img, 0, 0, width, height, 0, 0, width, height);
|
|
160
|
+
return canvas.toDataURL(IMAGE_TYPES.PNG, 1);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 通过图片地址获取对应 canvas
|
|
166
|
+
* @param imgSrc
|
|
167
|
+
* @returns
|
|
168
|
+
*/
|
|
169
|
+
export function getCanvas(imgSrc: string): Promise<HTMLCanvasElement> {
|
|
170
|
+
return getImgEle(imgSrc).then((img) => {
|
|
171
|
+
const canvas = document.createElement("canvas");
|
|
172
|
+
let { width, height } = getDomWH(img);
|
|
173
|
+
setDomWH(canvas, width, height);
|
|
174
|
+
const ctx = canvas.getContext("2d");
|
|
175
|
+
ctx && ctx.drawImage(img, 0, 0, width, height, 0, 0, width, height);
|
|
176
|
+
return canvas;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* 通过图片地址获取对应 canvas
|
|
182
|
+
* @param img
|
|
183
|
+
* @returns
|
|
184
|
+
*/
|
|
185
|
+
export function getCanvasByImg(img: HTMLImageElement): HTMLCanvasElement {
|
|
186
|
+
const canvas = document.createElement("canvas");
|
|
187
|
+
let { width, height } = getDomWH(img);
|
|
188
|
+
setDomWH(canvas, width, height);
|
|
189
|
+
const ctx = canvas.getContext("2d");
|
|
190
|
+
ctx && ctx.drawImage(img, 0, 0, width, height, 0, 0, width, height);
|
|
191
|
+
return canvas;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 截取图片
|
|
196
|
+
* @param img
|
|
197
|
+
* @param opt
|
|
198
|
+
* @param opt.startX
|
|
199
|
+
* @param opt.startY
|
|
200
|
+
* @param opt.width
|
|
201
|
+
* @param opt.height
|
|
202
|
+
* @returns Promise<string>
|
|
203
|
+
*/
|
|
204
|
+
export function cropImage(
|
|
205
|
+
img: HTMLImageElement | HTMLCanvasElement,
|
|
206
|
+
opt: { startX: number; startY: number; width: number; height: number },
|
|
207
|
+
) {
|
|
208
|
+
return cropImageToCanvas(img, opt).toDataURL(IMAGE_TYPES.PNG, 1);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 截取图片
|
|
213
|
+
* @param img
|
|
214
|
+
* @param opt
|
|
215
|
+
* @param opt.startX
|
|
216
|
+
* @param opt.startY
|
|
217
|
+
* @param opt.width
|
|
218
|
+
* @param opt.height
|
|
219
|
+
* @returns Promise<HTMLCanvasElement>
|
|
220
|
+
*/
|
|
221
|
+
export function cropImageToCanvas(
|
|
222
|
+
img: HTMLImageElement | HTMLCanvasElement,
|
|
223
|
+
opt: { startX: number; startY: number; width: number; height: number },
|
|
224
|
+
) {
|
|
225
|
+
const { startX, startY, width, height } = opt;
|
|
226
|
+
const canvas = document.createElement("canvas");
|
|
227
|
+
const ctx = canvas.getContext("2d");
|
|
228
|
+
// 设置 canvas 的宽度和高度为裁剪后的尺寸
|
|
229
|
+
canvas.width = width; // 裁剪后的宽度
|
|
230
|
+
canvas.height = height; // 裁剪后的高度
|
|
231
|
+
// 在 canvas 上绘制裁剪后的图像
|
|
232
|
+
ctx?.drawImage(img, startX, startY, width, height, 0, 0, width, height);
|
|
233
|
+
// 可以将裁剪后的图像数据获取出来
|
|
234
|
+
return canvas;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 绘制图片
|
|
239
|
+
* @param img
|
|
240
|
+
* @param opt
|
|
241
|
+
* @param opt.startX
|
|
242
|
+
* @param opt.startY
|
|
243
|
+
* @param opt.width
|
|
244
|
+
* @param opt.height
|
|
245
|
+
* @returns Promise<string>
|
|
246
|
+
*/
|
|
247
|
+
export function imgToCanvas(img: HTMLImageElement) {
|
|
248
|
+
const canvas = document.createElement("canvas");
|
|
249
|
+
const ctx = canvas.getContext("2d");
|
|
250
|
+
canvas.width = img.width;
|
|
251
|
+
canvas.height = img.height;
|
|
252
|
+
ctx?.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
|
|
253
|
+
return canvas;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 缩放图片
|
|
258
|
+
* @param img
|
|
259
|
+
* @param opt
|
|
260
|
+
* @returns base64
|
|
261
|
+
*/
|
|
262
|
+
export function scaleImage(img: HTMLImageElement | HTMLCanvasElement, opt = { scaleFactor: 1 }) {
|
|
263
|
+
return scaleImageToCanvas(img, opt).toDataURL(IMAGE_TYPES.PNG, 1);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* 缩放图片,返回 canvas
|
|
268
|
+
* @param img
|
|
269
|
+
* @param opt
|
|
270
|
+
* @returns canvas
|
|
271
|
+
*/
|
|
272
|
+
export function scaleImageToCanvas(img: HTMLImageElement | HTMLCanvasElement, opt = { scaleFactor: 1 }) {
|
|
273
|
+
const { scaleFactor = 1 } = opt;
|
|
274
|
+
const canvas = document.createElement("canvas");
|
|
275
|
+
const ctx = canvas.getContext("2d");
|
|
276
|
+
canvas.width = img.width * scaleFactor;
|
|
277
|
+
canvas.height = img.height * scaleFactor;
|
|
278
|
+
ctx?.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
|
|
279
|
+
return canvas;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 图片转为 base64
|
|
284
|
+
* @param img
|
|
285
|
+
* @returns
|
|
286
|
+
*/
|
|
287
|
+
export async function imgToBase64(img: string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement) {
|
|
288
|
+
if (!img || img instanceof Blob) {
|
|
289
|
+
return img;
|
|
290
|
+
}
|
|
291
|
+
let _img = img;
|
|
292
|
+
if (img instanceof Image) {
|
|
293
|
+
_img = img.src;
|
|
294
|
+
} else if (img instanceof HTMLVideoElement) {
|
|
295
|
+
// 从视频中获取图片
|
|
296
|
+
const canvas = document.createElement("canvas");
|
|
297
|
+
let { width, height } = getDomWH(img);
|
|
298
|
+
setDomWH(canvas, width, height);
|
|
299
|
+
const ctx = canvas.getContext("2d");
|
|
300
|
+
ctx && ctx.drawImage(img, 0, 0, width, height, 0, 0, width, height);
|
|
301
|
+
_img = canvas.toDataURL(IMAGE_TYPES.PNG, 1);
|
|
302
|
+
} else if (img instanceof HTMLCanvasElement) {
|
|
303
|
+
_img = img.toDataURL(IMAGE_TYPES.PNG, 1);
|
|
304
|
+
} else if (typeof _img === "string" && !_img.startsWith("data:image/")) {
|
|
305
|
+
// 加载图片,并获取图片 base64
|
|
306
|
+
_img = await getImgBase64(_img);
|
|
307
|
+
}
|
|
308
|
+
return _img;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* 图片转为 Blob
|
|
313
|
+
* @param img
|
|
314
|
+
* @returns
|
|
315
|
+
*/
|
|
316
|
+
export async function imgToBlob(img: string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement): Promise<Blob> {
|
|
317
|
+
if (!img) {
|
|
318
|
+
throw new Error("请传入图片数据");
|
|
319
|
+
}
|
|
320
|
+
if (img instanceof Blob) {
|
|
321
|
+
return Promise.resolve(img);
|
|
322
|
+
}
|
|
323
|
+
const _img = await imgToBase64(img);
|
|
324
|
+
if (typeof _img !== "string") {
|
|
325
|
+
throw new Error("传入的图片数据不正确");
|
|
326
|
+
}
|
|
327
|
+
return dataURItoBlob(_img);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* 获取图片的 base64、blob 格式
|
|
332
|
+
* @param img
|
|
333
|
+
*/
|
|
334
|
+
export async function getAllTypeImg(img: string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement) {
|
|
335
|
+
const base64 = await imgToBase64(img);
|
|
336
|
+
return {
|
|
337
|
+
base64,
|
|
338
|
+
blob: typeof base64 === "string" && dataURItoBlob(base64),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* 等比缩放图片尺寸(不裁切、不变形)
|
|
347
|
+
* @param {HTMLImageElement} img
|
|
348
|
+
* @param {number} scale 缩放比例 0~1
|
|
349
|
+
* @returns {Promise<Blob>}
|
|
350
|
+
*/
|
|
351
|
+
export function drawScaleImage(img, scale) {
|
|
352
|
+
return new Promise((resolve) => {
|
|
353
|
+
const w = Math.round(img.width * scale);
|
|
354
|
+
const h = Math.round(img.height * scale);
|
|
355
|
+
|
|
356
|
+
const canvas = document.createElement("canvas");
|
|
357
|
+
canvas.width = w;
|
|
358
|
+
canvas.height = h;
|
|
359
|
+
const ctx = canvas.getContext("2d");
|
|
360
|
+
|
|
361
|
+
// 整图等比绘制,无裁切
|
|
362
|
+
ctx.drawImage(img, 0, 0, w, h);
|
|
363
|
+
|
|
364
|
+
canvas.toBlob((blob) => resolve(blob), "image/png");
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* JPG/WebP 仅动态降画质(不改尺寸)
|
|
370
|
+
* @param {Blob} blob
|
|
371
|
+
* @param {HTMLImageElement} img
|
|
372
|
+
* @returns {Promise<Blob>}
|
|
373
|
+
*/
|
|
374
|
+
export function compressByQuality(blob, img, maxSize) {
|
|
375
|
+
return new Promise((resolve) => {
|
|
376
|
+
const canvas = document.createElement("canvas");
|
|
377
|
+
canvas.width = img.width;
|
|
378
|
+
canvas.height = img.height;
|
|
379
|
+
const ctx = canvas.getContext("2d");
|
|
380
|
+
ctx.drawImage(img, 0, 0);
|
|
381
|
+
|
|
382
|
+
let quality = 0.98;
|
|
383
|
+
const minQ = 0.5;
|
|
384
|
+
|
|
385
|
+
function loop() {
|
|
386
|
+
canvas.toBlob(
|
|
387
|
+
(newBlob) => {
|
|
388
|
+
if (newBlob.size <= maxSize || quality <= minQ) {
|
|
389
|
+
resolve(newBlob);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
quality -= 0.02;
|
|
393
|
+
loop();
|
|
394
|
+
},
|
|
395
|
+
blob.type,
|
|
396
|
+
quality,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
loop();
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* 智能压缩入口
|
|
405
|
+
* 1. 小于10M直接返回原图
|
|
406
|
+
* 2. JPG/WebP:只降画质,不改尺寸
|
|
407
|
+
* 3. PNG:不转格式、只等比缩分辨率,不裁切
|
|
408
|
+
* @param {Blob} blob
|
|
409
|
+
* @returns {Promise<Blob>}
|
|
410
|
+
*/
|
|
411
|
+
export function smartImageCompress(blob, maxSize) {
|
|
412
|
+
if (blob.size <= maxSize) {
|
|
413
|
+
return Promise.resolve(blob);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return new Promise((resolve) => {
|
|
417
|
+
const isPng = blob.type === "image/png";
|
|
418
|
+
const img = new Image();
|
|
419
|
+
|
|
420
|
+
img.src = URL.createObjectURL(blob);
|
|
421
|
+
img.onload = async () => {
|
|
422
|
+
URL.revokeObjectURL(img.src);
|
|
423
|
+
|
|
424
|
+
// 非PNG:只降画质
|
|
425
|
+
if (!isPng) {
|
|
426
|
+
const resBlob = await compressByQuality(blob, img, maxSize);
|
|
427
|
+
return resolve(resBlob);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// PNG:只等比缩分辨率,不转格式、不裁切
|
|
431
|
+
let scale = 0.95; // 每次缩小5%
|
|
432
|
+
const minScale = 0.5; // 最低缩到原图50%,不再缩
|
|
433
|
+
|
|
434
|
+
let currentBlob = blob;
|
|
435
|
+
|
|
436
|
+
const loopScale = async () => {
|
|
437
|
+
currentBlob = await drawScaleImage(img, scale);
|
|
438
|
+
|
|
439
|
+
if (currentBlob.size <= maxSize || scale <= minScale) {
|
|
440
|
+
resolve(currentBlob);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
scale -= 0.05;
|
|
445
|
+
|
|
446
|
+
loopScale();
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
loopScale();
|
|
450
|
+
};
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
|