本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
前言
工作中经常遇到图片的各种转换的需求,比如转成base64用于储存、转成blob上传文件、转成url在页面上回显。
有些方法同样适用于其它一般文件,比如转换后数据传输用、download url用等。
url to base64
用canvas转换。
function url2Base64(url, type = 'image/jpeg') {
return new Promise((resolve, reject) => {
const img = new Image()
const canvas = document.createElement('canvas');
img.crossOrigin = '*';
img.onload = function () {
const width = img.width, height = img.height;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, width, height);
const base64 = canvas.toDataURL(type);
resolve(base64);
};
img.onerror = function () {
reject(new Error('message'));
};
img.src = url;
});
}
canvas.toDataURL
方法第一个参数可以定义转换类型,默认image/png
,如果原图是jpg,转换为png后size会变大。base64的图片实际size可以通过base64.length * 3 / 4
来换算成字节长度。
所以最好是可以根据原图类型来定义转换格式,或者直接定义默认为image/jpeg
。
另外如果原图有透明区域,部分情况转换后会回显为黑色,这时候可以给canvas默认设置底色为白色。
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
blob to base64
function blob2Base64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener('load', () => {
const base64 = reader.result?.toString() || '';
resolve(base64);
});
reader.addEventListener('error', () => {
reject(new Error('message'));
});
reader.readAsDataURL(blob);
});
}
也可以尝试用此方式把url转成base64.
调用:
const onChange = async () => {
const blob = inputRef.current.files[0];
const base64 = await blob2Base64(blob)
};
<input ref={inputRef} type="file" onChange={onChange} />
url to blob
通过http请求获取blob对象。
function url2Blob(url) {
return window.fetch(url).then(response => response.blob());
}
blog to url
就一个api URL.createObjectURL
,返回值是url string,格式大概是blob:http://localhost:3000/56a79464-d044-4160-8188-a74f58128786
,blob:
开头,后面跟着当前网站域名加一个guid。这个url的生命周期和创建它的窗口中的document绑定,即刷新页面、关闭页面就会被自动释放了,就失效了。可以当做页面图片回显或者download link用,不能用作存储。
这种URL形式回显在页面中,会大幅提高性能,尤其是图片非常多的情况。但是要注意释放时机。
developer.mozilla.org/zh-CN/docs/… 这里有详解。
function blob2Url(blob) {
return URL.createObjectURL(blob);
}
base64 to url
先转成blob,然后再转url。
function base642Url(data) {
var parts = data.split(';base64,'),
contentType = parts[0].split(':')[1],
raw = window.atob(parts[1]),
length = raw.length,
arr = new Uint8Array(length);
for (var i = 0; i < length; i++) {
arr[i] = raw.charCodeAt(i);
}
var blob = new Blob([arr], { type: contentType });
return URL.createObjectURL(blob);
};
image crop
图片局部截图功能,输出base64。
const TO_RADIANS = Math.PI / 180;
function crop2Blob(image, crop, scale = 1, rotate = 0) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('No 2d context');
}
const scaleX = image.naturalWidth / image.width;
const scaleY = image.naturalHeight / image.height;
const pixelRatio = window.devicePixelRatio || 1
canvas.width = Math.floor(crop.width * scaleX * pixelRatio);
canvas.height = Math.floor(crop.height * scaleY * pixelRatio);
ctx.scale(pixelRatio, pixelRatio);
ctx.imageSmoothingQuality = 'high';
const cropX = crop.x * scaleX;
const cropY = crop.y * scaleY;
const rotateRads = rotate * TO_RADIANS;
const centerX = image.naturalWidth / 2;
const centerY = image.naturalHeight / 2;
ctx.save();
ctx.translate(-cropX, -cropY);
ctx.translate(centerX, centerY);
ctx.rotate(rotateRads);
ctx.scale(scale, scale);
ctx.translate(-centerX, -centerY);
ctx.drawImage(image, 0, 0, image.naturalWidth, image.naturalHeight, 0, 0, image.naturalWidth, image.naturalHeight);
ctx.restore();
const result = canvas.toDataURL('image/jpeg'); // 需要注意输出格式
return result;
}
参数讲解:
image
Image类型对象,也就是img标签的DOM对象。如果没有,crop
横纵轴坐标及高宽。scale
缩放,默认不缩放。>1 放大,<1 缩小。rotate
旋转角度,按中心点顺时针旋转,比如45
即顺时针旋转45度。
例子:截取左上角300*300区域图片。
const base64 = await crop2Blob(imgRef.current, {
width: 300,
height: 300,
x: 0,
y: 0,
});
<img src="..." ref={imgRef} />
如果只有url或base64变量,且当前HTML里没有定义的img DOM,可以手动new一个。需要load之后再传参,不然取不到图片高宽。
Convert url\base64 to Image DOM
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = src;
img.onload = () => {
resolve(img);
};
img.onError = e => {
reject(e);
};
});
}
最后
通过上面的实现发现,canvas在图片处理中用到的比较多,还可以用来压缩图片、拼接图片等各种操作。
上面的实现可能并不是最优方法,可以在有类似需求时当做参考。