基本思路
- 监听 input file 的 change 事件,获取到上传的文件信息
- 使用 FileReader 对象读取文件的 base64 格式,赋值给 img 显示
- 将图片绘制在 canvas 上,控制图片绘制的宽度,超过宽度,等比缩放。
- 利用 exif.js 获取图片的方向信息,解决 ios 上竖直照片翻转
基础代码
你只需要一个简简单单的 input 即可,当然,要显示预览图片,肯定还需要一个 img 标签。此外,原生的 input 标签比较丑,为了美观起见,一般都会把 input 透明度设置成 0,然后自定义个按钮的样式盖在上面,样式这里就不详细讲了,easy。
<input type="file" name="image" accept=“image/*” onchange='handleInputChange' id="upload-img">
<img src="" class="prev-img">
兼容性
- 安卓上不支持 multiple 属性,即不能一次多选
- 因为调用的是系统原生的文件读取,每个机型显示的页面不一样,难以测试
完整的封装 Class 如下,如果不需要压缩或者方向处理,直接注释即可。
export default class Uploader {
constructor() {
}
/**
* 把base64格式转化成Blob格式
* @param {[type]} urlData [description]
* @return {[type]} [description]
*/
convertBase64UrlToBlob(urlData) {
var arr = urlData.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {
type: mime
});
}
/**
* 外部调用的方法 完成图片的压缩
* @param {[type]} file 需要处理的file对象
* @param {[type]} obj 传入的配置 quality 压缩的质量
* @param {Function} callback 完成压缩之后的回调函数
* @return {[type]} [description]
*/
photoCompress(file, obj, callback) {
// 使用HTML5的FileReader开始读取File对象中的内容
let setting = obj;
var ready = new FileReader();
ready.readAsDataURL(file);
var that = this;
// 判断图片需不需要压缩
if (file.size / 1024 < 500) {
setting.quality = .9;
} else {
setting.quality = .5;
}
// 获取图片源信息 判断照片方向
ready.onload = function () {
var _this2 = this;
that.getOrientation(file).then(function(o){
// setting.orientation = EXIF.getTag(file, "Orientation");
setting.orientation = o;
var re = _this2.result;
that.canvasDataURL(re, setting, callback, file)
})
}
}
/**
* canvas绘制
* @param {[type]} result [description]
* @param {[type]} obj [description]
* @param {Function} callback [description]
* @return {[type]} [description]
*/
canvasDataURL(result, obj, callback, file) {
var img = new Image();
img.src = result;
var _this = this;
img.onload = function() {
var that = this;
var w, h, scale, quality;
scale = that.width / that.height;
if (that.width > 1200) {
w = 1200;
h = w / scale;
} else {
w = that.width;
h = that.height;
}
//生成canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 创建属性节点
var anw = document.createAttribute("width");
anw.nodeValue = w;
var anh = document.createAttribute("height");
anh.nodeValue = h;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
// 调整图片方向
// console.log(obj.orientation)
if (obj.orientation && obj.orientation != "" && obj.orientation != 1) {
switch (obj.orientation) {
case 6: //需要顺时针(向左)90度旋转
_this.rotateImg(that, w, h, 'left', canvas, ctx);
break;
case 8: //需要逆时针(向右)90度旋转
_this.rotateImg(that, w, h, 'right', canvas, ctx);
break;
case 3: //需要180度旋转
_this.rotateImg(that, w, h, 'right', canvas, ctx); //转两次
_this.rotateImg(that, w, h, 'right', canvas, ctx);
break;
}
} else {
ctx.drawImage(that, 0, 0, w, h);
}
// 压缩图像质量
var base64 = canvas.toDataURL('image/jpeg', obj.quality);
// 回调函数返回base64的值
callback(base64);
}
}
getObjectURL(file) {
var url = null;
if (window.createObjectURL != undefined) {
url = window.createObjectURL(file);
} else if (window.URL != undefined) { // mozilla(firefox)
url = window.URL.createObjectURL(file);
} else if (window.webkitURL != undefined) { // webkit or chrome
url = window.webkitURL.createObjectURL(file);
}
return url;
}
rotateImg(img, w, h, direction, canvas, ctx) {
//最小与最大旋转方向,图片旋转4次后回到原方向
var min_step = 0;
var max_step = 3;
//var img = document.getElementById(pid);
if (img == null) return;
//img的高度和宽度不能在img元素隐藏后获取,否则会出错
var height = h;
var width = w;
//var step = img.getAttribute('step');
var step = 2;
if (step == null) {
step = min_step;
}
if (direction == 'right') {
step++;
//旋转到原位置,即超过最大值
step > max_step && (step = min_step);
} else {
step--;
step < min_step && (step = max_step);
}
//旋转角度以弧度值为参数
var degree = step * 90 * Math.PI / 180;
// var ctx = canvas.getContext('2d');
// alert(h)
switch (step) {
case 0:
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0);
break;
case 1:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, 0, -height,w,h);
break;
case 2:
canvas.width = width;
canvas.height = height;
ctx.rotate(degree);
ctx.drawImage(img, -width, -height,w,h);
break;
case 3:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, -width, 0,w,h);
break;
}
}
ajaxThen(data) {
return new Promise((resolve, reject) => {
var form = new FormData();
form.append('image', data);
// console.log(data,form)
$.ajax({
url: '/home/image',
data: form,
method: 'post',
dataType: 'json',
contentType: false,
processData: false,
mimeType: "multipart/form-data",
success: function(res) {
resolve(res)
}
})
})
}
getOrientation(file) {
return new Promise((resolve, reject) => {
EXIF.getData(file, () => {
let o = EXIF.getTag(file, "Orientation");
resolve(o);
reject(o);
})
})
}
}