uniapp雖然在某種情況下是個(gè)很好用的框架澜汤,但還是避免不了踩坑炎滞,而坑在社區(qū)或官網(wǎng)或百度里都沒法找到能立即適用的解決方案敢艰。今天遇到的一個(gè)大問題就是使用uni.chooseImage上傳圖片或者拍照在部分手機(jī)上圖片出現(xiàn)旋轉(zhuǎn),同時(shí)就算設(shè)置了壓縮結(jié)局還是沒有壓縮圖片的問題册赛。
今天我給大家一份完整的解決方案钠导,就目前運(yùn)行的項(xiàng)目來說,oppo, 小米森瘪,蘋果的手機(jī)出現(xiàn)旋轉(zhuǎn)角度有順時(shí)針牡属,逆時(shí)針90度以及旋轉(zhuǎn)180度等問題,目前測試均無問題扼睬。
因時(shí)間問題逮栅,有些地方代碼不一定是最簡潔的悴势,大家如果有更好的方式歡迎大家留言,共同學(xué)習(xí)進(jìn)步措伐。
第一步特纤,我們必須要知道圖片旋轉(zhuǎn)的方向(exifjs庫是可以拿到圖片的很多信息包括方向,但在uniapp中無法使用侥加,所以這里沒有用捧存,但大家可以嘗試),uni.getImageInfo號稱是可以拿到該值担败,但經(jīng)過實(shí)驗(yàn)沒有拿到昔穴,所以我換了種方式,該方式其實(shí)在網(wǎng)上也可搜索到提前,我們直接看源碼:
export const common = {
// 這里的獲取exif要將圖片轉(zhuǎn)ArrayBuffer對象吗货,這里假設(shè)獲取了圖片的baes64
// 步驟一
// base64轉(zhuǎn)ArrayBuffer對象
base64ToArrayBuffer: (base64) => {
base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
var binary = atob(base64); //atob() 方法用于解碼使用 base-64 編碼的字符串,windows對象里的方法
var len = binary.length;
var buffer = new ArrayBuffer(len); // 創(chuàng)建一個(gè)binary.length字節(jié)的ArrayBuffer
var view = new Uint8Array(buffer);//創(chuàng)建一個(gè)buffer的引用狈网,類型是Uint8卿操,起始位置在0,結(jié)束位置為緩沖區(qū)尾部
for (var i = 0; i < len; i++) {
view[i] = binary.charCodeAt(i); //在引用指定位置里存入指定位置的字符的 Unicode 編碼
}
return buffer; //最終生成一個(gè)數(shù)組緩沖區(qū)孙援,里面存儲(chǔ)了該base64圖片的二進(jìn)制信息 害淤,為了后續(xù)通過底層接口拿到多種數(shù)值類型
},
// 步驟二,Unicode碼轉(zhuǎn)字符串
// ArrayBuffer對象 Unicode碼轉(zhuǎn)字符串
getStringFromCharCode: (dataView, start, length) => {
var str = '';
var i;
for (i = start, length += start; i < length; i++) {
str += String.fromCharCode(dataView.getUint8(i));
}
return str; //將第一步中的二進(jìn)制ArrayBuffer對象生成的unicode的碼轉(zhuǎn)成字符串
},
// 步驟三拓售,獲取jpg圖片的exif的角度(在ios體現(xiàn)最明顯)下面很多都是為了獲取圖片數(shù)值的底層信息窥摄,陌生的大家可以一個(gè)個(gè)查,或者去看extjs官網(wǎng)
getOrientation: (arrayBuffer) => {
var dataView = new DataView(arrayBuffer); //可以從 二進(jìn)制[`ArrayBuffer`]對象中讀寫多種數(shù)值類型的底層接口
var length = dataView.byteLength;
var orientation;
var exifIDCode;
var tiffOffset;
var firstIFDOffset;
var littleEndian;
var endianness;
var app1Start;
var ifdStart;
var offset;
var i;
// Only handle JPEG image (start by 0xFFD8)
if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
offset = 2;
while (offset < length) {
if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
app1Start = offset;
break;
}
offset++;
}
}
if (app1Start) {
exifIDCode = app1Start + 4;
tiffOffset = app1Start + 10;
if (common.getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
endianness = dataView.getUint16(tiffOffset);
littleEndian = endianness === 0x4949;
if (littleEndian || endianness === 0x4D4D /* bigEndian */ ) {
if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
if (firstIFDOffset >= 0x00000008) {
ifdStart = tiffOffset + firstIFDOffset;
}
}
}
}
}
if (ifdStart) {
length = dataView.getUint16(ifdStart, littleEndian);
var standalone = window.navigator.standalone,
userAgent = window.navigator.userAgent.toLowerCase(),
safari = /safari/.test(userAgent),
ios = /iphone|ipod|ipad/.test(userAgent);
for (i = 0; i < length; i++) {
offset = ifdStart + i * 12 + 2;
if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */ ) {
// 8 is the offset of the current tag's value
offset += 8;
// Get the original orientation value
orientation = dataView.getUint16(offset, littleEndian);
// Override the orientation with its default value for Safari (#120)
// if (IS_SAFARI_OR_UIWEBVIEW) {
// dataView.setUint16(offset, 1, littleEndian);
// }
if (ios) {
if (!standalone && safari) {
dataView.setUint16(offset, 1, littleEndian);
} else if (standalone && !safari) {
//standalone
} else if (!standalone && !safari) {
dataView.setUint16(offset, 1, littleEndian);
};
}
break;
}
}
}
return orientation;
}
}
第二步础淤,獲取圖片的旋轉(zhuǎn)方向并壓縮:
urlTobase64(url) {
uni.request({
url: url,
method: 'GET',
responseType: 'arraybuffer',
success: (res)=> {
let base64 = uni.arrayBufferToBase64(res.data); //把a(bǔ)rraybuffer轉(zhuǎn)成base64
base64 = 'data:image/jpeg;base64,' + base64; //不加上這串字符崭放,在頁面無法顯示
const or=common.getOrientation(common.base64ToArrayBuffer(base64))
this.or=or
this.compressImg(base64,or)
}
});
},
第三步,對需要旋轉(zhuǎn)的圖片旋轉(zhuǎn)鸽凶,壓縮币砂,這里設(shè)置的canvas的width=300,height=400。里面值需要以此為參考換算:
compressImg(img,or){
const ctx = uni.createCanvasContext('myCanvas')
console.log(or)
if(or==6){//逆時(shí)針旋轉(zhuǎn)了90
ctx.translate(300,0)
ctx.rotate(Math.PI/2)
ctx.drawImage(img, 0, 0,400, 300)
}else if(or==3){//逆時(shí)針旋轉(zhuǎn)了180
ctx.translate(300,400)
ctx.rotate(Math.PI)
ctx.drawImage(img, 0, 0,400, 300)
}else if(or==8){//順時(shí)針旋轉(zhuǎn)90
ctx.translate(0,400)
ctx.rotate(-Math.PI/2)
ctx.drawImage(img, 0, 0,400, 300)
}else{
ctx.drawImage(img, 0, 0,300, 400)
}
ctx.draw(false,()=>{
uni.canvasToTempFilePath({
x: 0,
y: 0,
width: 300,
height:400,
destWidth:300,
destHeight:400,
fileType:'jpg',
canvasId: 'myCanvas',
success:(res)=> {
this.img=res.tempFilePath
},
fail:(err)=>{
this.img=img
}
})
})
},
第四步玻侥,通過uni.chooseImage上傳圖片:
// 選擇圖片
ChooseImage() {
uni.showLoading({
title: '照片獲取中',
mask: true
});
uni.chooseImage({
count: 1, //默認(rèn)9
sizeType: ['compressed'], //可以指定是原圖還是壓縮圖决摧,默認(rèn)二者都有
sourceType: ['album', 'camera'], //從相冊選擇
success: (res) => {
this.urlTobase64(res.tempFilePaths[0])
uni.hideLoading()
},
fail:(err)=>{
uni.hideLoading()
}
});
},
至此,一套完整的方案就已經(jīng)產(chǎn)生了凑兰。希望能幫到需要的朋友掌桩。