vue之图片上传解决图片压缩和拍摄的角度旋转的问题
参考Exif.js 提供了 JavaScript 读取图像的原始数据的功能扩展,例如:拍照方向、相机设备型号、拍摄时间、ISO 感光度、GPS 地理位置等数据。(地址:http://code.ciaoca.com/javascript/exif-js/),结合vue的使用,兼容pc端和移动端,实现单张或者多张图片上传,图片压缩,解决拍摄角度旋转的问题。
效果图:
vue文件:
<template>
<div class="add_tuzhi add_tuzhi_textarea">
<div class="add_tuzhi_com mb bg-fff">
<input id="plus_sent_pic" type="file" @change="handleFileChange" ref="inputer" class="trans_file" accept="image/png, image/jpeg, image/gif, image/jpg" multiple>
<div class="add_img clearfix">
<div class="add_img_list" v-for="(data,index) in imgData" @click="delImg(index)">
<img :src="data" >
<span class="tuzhi_sel"></span>
</div>
<template v-if="img_loading">
<div class="add_img_list">
<p class="map_loading">图片上传中</p>
</div>
</template>
<template v-else>
<label for="plus_sent_pic" class="add_img_list" v-if="num<9">
<div class="tuzhi_add">
<p><span>{{num}}</span>/9</p>
</div>
</label>
</template>
</div>
</div>
</div>
</template>
<script>
import EXIF from '../../common/js/small-exif.js';
export default {
data () {
return {
num:0, //上传图片数量
base64:'' , //压缩后的图片
imgData:[], //图片
data:{base64:''},
imgType:2, //图片上传的状态 0:图片已经成功上传 1表示图片在上传中 2表示图片还没上传,
img_loading:false,
Orientation:'' , //图片的拍摄角度
}
},
mounted:function(){
},
methods:{
delImg(id,index){ //删除图片
this.imgData.splice(index, 1);
if(this.num>=1){
this.num-=1;
}
},
handleFileChange () { //获取图像
let that=this;
let inputDOM = this.$refs.inputer;
// 通过DOM取文件数据
for(let i in inputDOM.files){
this.file= inputDOM.files[i];
this.imgPreview(this.file);
EXIF.getData(this.file, function() {
that.Orientation = EXIF.getTag(this, 'Orientation');
});
}
},
imgPreview (file) { //base64 格式
this.imgType=1;
this.img_loading=true;
let self = this;
let imgContent={};
imgContent.name=file.name;
// 看支持不支持FileReader
if (!file || !window.FileReader) return;
if (/^image/.test(file.type)) {
// 创建一个reader
var reader = new FileReader();
// 将图片将转成 base64 格式
reader.readAsDataURL(file);
// 读取成功后的回调
reader.onloadend = function () {
let IMG = new Image();
IMG.src = this.result;
IMG.onload = function(){
let w = this.naturalWidth,
h = this.naturalHeight,
resizeW = 0,
resizeH = 0;
//压缩设置
let maxSize = {
width:1280, //图片最大宽度
height:1280, //图片最大高度
level:0.6 //图片保存质量
};
//计算缩放比例
if(w > maxSize.width || h > maxSize.height){
let multiple = Math.max(w / maxSize.width , h / maxSize.height);
resizeW = w / multiple;
resizeH = h / multiple;
}else{
resizeW = w;
resizeH = h;
}
let canvas = document.createElement("canvas"),
cxt = canvas.getContext('2d');
//根据拍摄的角度进行图片旋转调整
if (self.Orientation == 3) {
canvas.width = resizeW;
canvas.height = resizeH;
cxt.rotate(Math.PI);
cxt.drawImage(IMG, 0, 0, -resizeW, -resizeH)
} else if (self.Orientation == 8) {
canvas.width = resizeH;
canvas.height = resizeW;
cxt.rotate(Math.PI * 3 / 2);
cxt.drawImage(IMG, 0, 0, -resizeW, resizeH)
} else if (self.Orientation == 6) {
canvas.width = resizeH;
canvas.height = resizeW;
cxt.rotate(Math.PI / 2);
cxt.drawImage(IMG, 0, 0, resizeW, -resizeH)
} else {
canvas.width = resizeW;
canvas.height = resizeH;
cxt.drawImage(IMG, 0, 0, resizeW, resizeH)
}
//base64,最终输出的压缩文件
self.base64 = canvas.toDataURL('image/jpeg',maxSize.level);
self.num+=1;
self.imgType=0;
self.img_loading=false;
self.imgData.push(self.base64 )
}
};
}
}
}
}
</script>
<style scoped src="./approach_add.css">
</style>
css文件:
@charset "utf-8";
.add_tuzhi{
height: 100%;
overflow-y: scroll;
padding-bottom: 2rem;
}
.trans_file{
display: none;
}
.add_img{
padding: 0.213333rem;
}
.add_img_list{
float: left;
width: 3.893333333333333rem;
height: 3.893333333333333rem;
position: relative;
padding: 0.213333rem;
box-sizing: border-box;
}
.add_img_list img{
width: 100%;
height: 100%;
}
.add_img_list .tuzhi_sel{
position: absolute;
width: 0.64rem;
height: 0.64rem;
right:0;
top:0;
background: url(./colse.png);
background-size: 100% 100%;
}
.add_img_list .tuzhi_add{
display: block;
width:100%;
height: 100%;
position: relative;
background:url(./add.png);
background-size: 100% 100%;
}
.add_img_list .tuzhi_add p{
color: #ccc;
font-size: 0.554667rem;
position: absolute;
left: 1.28rem;
bottom:0.32rem;
}
.add_img_list .map_loading{
width:100%;
text-align:center;
height: 3.466666666666333rem;
line-height: 3.466666666666333rem;
color:#333;
font-size: 0.554667rem;
border:1px #dedede dashed;
}
封装好的exif-js文件:
var EXIF = {};
var TiffTags = EXIF.TiffTags = {
0x0112: "Orientation"
};
function imageHasData(img) {
return !!(img.exifdata);
}
function getImageData(img, callback) {
function handleBinaryFile(binFile) {
var data = findEXIFinJPEG(binFile);
img.exifdata = data || {};
if (callback) {
callback.call(img);
}
}
if (window.FileReader && (img instanceof window.Blob || img instanceof window.File)) {
var fileReader = new FileReader();
fileReader.onload = function (e) {
handleBinaryFile(e.target.result);
};
fileReader.readAsArrayBuffer(img);
}
}
function findEXIFinJPEG(file) {
var dataView = new DataView(file);
if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
return false; // not a valid jpeg
}
var offset = 2,
length = file.byteLength,
marker;
while (offset < length) {
if (dataView.getUint8(offset) != 0xFF) {
return false; // not a valid marker, something is wrong
}
marker = dataView.getUint8(offset + 1);
if (marker == 225) {
return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2);
} else {
offset += 2 + dataView.getUint16(offset + 2);
}
}
}
function readTags(file, tiffStart, dirStart, strings, bigEnd) {
var entries = file.getUint16(dirStart, !bigEnd),
tags = {},
entryOffset, tag,
i;
for (i = 0; i < entries; i++) {
entryOffset = dirStart + i * 12 + 2;
tag = strings[file.getUint16(entryOffset, !bigEnd)];
tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
}
return tags;
}
function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
var type = file.getUint16(entryOffset + 2, !bigEnd),
numValues = file.getUint32(entryOffset + 4, !bigEnd),
valueOffset = file.getUint32(entryOffset + 8, !bigEnd) + tiffStart,
offset,
vals, val, n,
numerator, denominator;
switch (type) {
case 1: // byte, 8-bit unsigned int
case 7: // undefined, 8-bit byte, value depending on field
if (numValues == 1) {
return file.getUint8(entryOffset + 8, !bigEnd);
} else {
offset = numValues > 4 ? valueOffset : (entryOffset + 8);
vals = [];
for (n = 0; n < numValues; n++) {
vals[n] = file.getUint8(offset + n);
}
return vals;
}
case 2: // ascii, 8-bit byte
offset = numValues > 4 ? valueOffset : (entryOffset + 8);
return getStringFromDB(file, offset, numValues - 1);
case 3: // short, 16 bit int
if (numValues == 1) {
return file.getUint16(entryOffset + 8, !bigEnd);
} else {
offset = numValues > 2 ? valueOffset : (entryOffset + 8);
vals = [];
for (n = 0; n < numValues; n++) {
vals[n] = file.getUint16(offset + 2 * n, !bigEnd);
}
return vals;
}
case 4: // long, 32 bit int
if (numValues == 1) {
return file.getUint32(entryOffset + 8, !bigEnd);
} else {
vals = [];
for (n = 0; n < numValues; n++) {
vals[n] = file.getUint32(valueOffset + 4 * n, !bigEnd);
}
return vals;
}
case 5: // rational = two long values, first is numerator, second is denominator
if (numValues == 1) {
numerator = file.getUint32(valueOffset, !bigEnd);
denominator = file.getUint32(valueOffset + 4, !bigEnd);
val = new Number(numerator / denominator);
val.numerator = numerator;
val.denominator = denominator;
return val;
} else {
vals = [];
for (n = 0; n < numValues; n++) {
numerator = file.getUint32(valueOffset + 8 * n, !bigEnd);
denominator = file.getUint32(valueOffset + 4 + 8 * n, !bigEnd);
vals[n] = new Number(numerator / denominator);
vals[n].numerator = numerator;
vals[n].denominator = denominator;
}
return vals;
}
case 9: // slong, 32 bit signed int
if (numValues == 1) {
return file.getInt32(entryOffset + 8, !bigEnd);
} else {
vals = [];
for (n = 0; n < numValues; n++) {
vals[n] = file.getInt32(valueOffset + 4 * n, !bigEnd);
}
return vals;
}
case 10: // signed rational, two slongs, first is numerator, second is denominator
if (numValues == 1) {
return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset + 4, !bigEnd);
} else {
vals = [];
for (n = 0; n < numValues; n++) {
vals[n] = file.getInt32(valueOffset + 8 * n, !bigEnd) / file.getInt32(valueOffset + 4 + 8 * n, !bigEnd);
}
return vals;
}
}
}
function getStringFromDB(buffer, start, length) {
var outstr = "";
for (var n = start; n < start + length; n++) {
outstr += String.fromCharCode(buffer.getUint8(n));
}
return outstr;
}
function readEXIFData(file, start) {
if (getStringFromDB(file, start, 4) != "Exif") {
return false;
}
var bigEnd,
tags, tag,
exifData, gpsData,
tiffOffset = start + 6;
// test for TIFF validity and endianness
if (file.getUint16(tiffOffset) == 0x4949) {
bigEnd = false;
} else if (file.getUint16(tiffOffset) == 0x4D4D) {
bigEnd = true;
} else {
return false;
}
if (file.getUint16(tiffOffset + 2, !bigEnd) != 0x002A) {
return false;
}
var firstIFDOffset = file.getUint32(tiffOffset + 4, !bigEnd);
if (firstIFDOffset < 0x00000008) {
return false;
}
tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd);
return tags;
}
EXIF.getData = function (img, callback) {
if ((img instanceof Image || img instanceof HTMLImageElement) && !img.complete) return false;
if (!imageHasData(img)) {
getImageData(img, callback);
} else {
if (callback) {
callback.call(img);
}
}
return true;
}
EXIF.getTag = function (img, tag) {
if (!imageHasData(img)) return;
return img.exifdata[tag];
}
export default EXIF