前言
一般情况下前端使用canvas进行图片压缩,但是这样不能保留图片的旋转信息,导致压缩后图片旋转,下面我就讲述下如何保证图片正常旋转的。
代码
先祭献出代码啦
import Exif from "exif-js";
/**
* @desc 获取图片信息,使用exif.js库,具体用法请在github中搜索
* @param {Object} file 上传的图片文件
* @param {String} tag 需要获取的信息 例如:'Orientation'旋转信息
* @return {Promise<Any>} 读取是个异步操作,返回指定的图片信息
*/
const getImageTag = (file, tag) => {
return new Promise((resolve) => {
Exif.getData(file, function () {
const o = Exif.getTag(this, tag);
resolve(o);
});
});
};
/**
* @desc 获取旋转后的图片
* @param {Object} img 图片文件
* @param {Number} or 旋转信息
*/
const getRotateImg = (canvas, img, or, compval) => {
const ctx = canvas.getContext('2d');
// 使用图片的原始尺寸
let tH = img.naturalWidth * compval;
let tW = img.naturalHeight * compval;
canvas.width = tH;
canvas.height = tW;
ctx.drawImage(img, 0, 0, tH, tW);
switch (or) {
case 6: // 顺时针旋转90度
rotateImg(img, 'right', canvas);
break;
case 8: // 逆时针旋转90度
rotateImg(img, 'left', canvas);
break;
case 3: // 顺时针旋转180度
rotateImg(img, 'right', canvas, 2);
break;
default:
break;
}
return canvas.toDataURL('image/png', 1);
}
/**
* @desc 旋转canvas,会对源数据canvas进行修改
* @param {Object} img 图片文件
* @param {String} dir 方向 left逆时针|right顺时针
* @param {Object} canvas 画布
* @param {Number} s 向指定方向旋转几步,1步为90度
*/
const rotateImg = (img, dir = "right", canvas, s = 1) => {
const MIN_STEP = 0;
const MAX_STEP = 3;
const width = canvas.width || img.width;
const height = canvas.height || img.height;
let step = 0;
if (dir === "right") {
step += s;
step > MAX_STEP && (step = MIN_STEP);
} else {
step -= s;
step < MIN_STEP && (step = MAX_STEP);
}
const degree = (step * 90 * Math.PI) / 180;
const ctx = canvas.getContext("2d");
switch (step) {
case 1:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, 0, -height, width, height);
break;
case 2:
canvas.width = width;
canvas.height = height;
ctx.rotate(degree);
ctx.drawImage(img, -width, -height, width, height);
break;
case 3:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, -width, 0, width, height);
break;
default:
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
break;
}
};
/**
* 读取文件数据
*
* @param {HTMLFILE} [file=File]
*/
const GetFileData = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = async e => {
resolve(e.target.result);
};
});
}
/**
* 图片压缩功能
*
* @param {HTMLFILE} [file=File]
* @param {number} [targetSize=5 * 1024]
*/
const CompressImg = async (file, targetSize = 5 * 1024) => {
return new Promise(async (resolve, reject) => {
if (!file) {
reject('未传入文件');
}
// 判断文件格式
const isIMG =
[
"image/jpeg",
"image/jpeg",
"image/jpg",
"image/png",
"image/bmp"
].indexOf(file.type) >= 0;
if (!isIMG) {
reject('文件格式错误');
}
const FileBaseData = await GetFileData(file);
// 根据指定的目标大小进行压缩
let compVal = targetSize / FileBaseData.length;
compVal = compVal > 1 ? 1 : Math.floor(compVal);
let img = document.createElement('img');
let canvas = document.createElement('canvas');
img.onload = async () => {
// 获取图片的旋转信息
let or = await getImageTag(file, 'Orientation');
// 获取到压缩后的图片数据
let data = getRotateImg(canvas, img, or, compVal);
resolve(data);
}
img.src = FileBaseData
})
}
export default CompressImg;
原理
使用canvas直接压缩图片会把原图的属性信息全部删除掉,但是我们可以通过获取File对象中的属性,在执行压缩前执行旋转,这样子就能保证压缩后的图片依然能够正常预览了。这里我们使用了 exif.js 库,它可以方便的获取File对象的属性信息,讲到这里,下面的基本可以不用看了。
步骤
根据目标的文件大小获取压缩值
这里默认未5M大小,根据图片本身大小,小于 5M 则不进行压缩,大于5M则根据比例压缩,虽然实际使用存在较大误差,但是还是可以覆盖多数需求场景的。
const FileBaseData = await GetFileData(file);
// 根据指定的目标大小进行压缩
let compVal = targetSize / FileBaseData.length;
compVal = compVal > 1 ? 1 : Math.floor(compVal);
获取图片的旋转信息
const getImageTag = (file, tag) => {
return new Promise((resolve) => {
Exif.getData(file, function () {
const o = Exif.getTag(this, tag);
resolve(o);
});
});
};
执行旋转
使用了cavans的rotate方法对幕布进行旋转,同时也不要忘记绘入图片的时候对图片进行翻转。
const rotateImg = (img, dir = "right", canvas, s = 1) => {
const MIN_STEP = 0;
const MAX_STEP = 3;
const width = canvas.width || img.width;
const height = canvas.height || img.height;
let step = 0;
if (dir === "right") {
step += s;
step > MAX_STEP && (step = MIN_STEP);
} else {
step -= s;
step < MIN_STEP && (step = MAX_STEP);
}
const degree = (step * 90 * Math.PI) / 180;
const ctx = canvas.getContext("2d");
switch (step) {
case 1:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, 0, -height, width, height);
break;
case 2:
canvas.width = width;
canvas.height = height;
ctx.rotate(degree);
ctx.drawImage(img, -width, -height, width, height);
break;
case 3:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, -width, 0, width, height);
break;
default:
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
break;
}
};
很老之前的代码了,内容有部分借鉴别人的代码,已经忘记来源了,侵删