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