需求:
在项目中,我们经常要实现用户上传图像的功能,由于图像“太大”或者”压缩上传服务器”或者“本地处理图片”,我们经常会用canvas对图片进行2次处理。在最近的一次开发中我发现,ios低系统、ios(13.4.1之后)高系统对图片的旋转处理不同。并且我还研究了android及chrome对旋转图片的处理,下面我研究的成果:
H5旋转图片的显示对比 ,对比ios系统和android系统
我研究了 ios 低(低系统),ios高(ios高系统13.4.1之后),pc(chrome 84.0.4147.68),android系统。发现ios高和pc处理旋转是相同的。所以下面讲解ios高也代表pc。我用一张oritation为6的图片在不同系统进行了测试,结果如下:
- ios低: 将图片旋转为正确的方向显示
- ios高:将图片旋转为正确的方向显示
android: 图片旋转方向显示。
如下图所示:
![]()
正确方向(想要的方向) 方向旋转 ios低,ios高 android
canvas 绘制(drawImage)旋转图片处理,对比ios高和ios低
当我们看到上面的显示结果时,我们肯定想ios很智能啊,已经显示出我们想要的结果,其实ios真的很坑。对于图片我们上面说过会经常通过canvas进行处理,我们用canvas进行最简单的drawImage操作, 结果显示如下:
![]()
ios高 ios低,android 结论: ios低系统在canvas绘制的时候不是以显示的正方向绘制的图片,旋转信息影响着canvas 的绘制,ios高以正确的方向绘制。
通过“H5显示”和“drawImage”分析,结论如下
- ios和android对旋转的图片显示机制不同
- ios高和ios低 虽然显示相同但是canvas处理时,旋转图片的方向却不同
android和ios低的兼容处理
我们发现ios低和android系统,在canvas处理的时候都是以 “有旋转方向的图片” 处理的。所以我们的处理方式是,获取图片的旋转信息,canvas 根据旋转信息将图片进行相应的旋转处理,最终将canvas导出正确显示的图片:
function renderFileChangedImg(img, distImg) {
return new Promise(function (resolve) {
// MegaPixImage.js 大家可以在网上查找,用户处理图片旋转和大小
var mpImg = new MegaPixImage(img);
// img 为用户上传的图片 ,通过exif获取图片的信息
EXIF.getData(img, () => {
var allMetaData = EXIF.getAllTags(img);
// 获取图片的旋转信息
var orientation = allMetaData.Orientation;
mpImg.render(distImg, {
maxWidth: 1980,
maxHeight: 1980,
orientation: orientation
}, resolve)
})
})
}
我们获取到结果canvas处理后正确的图片后,我们可以回显 “该图片” ,解决android显示旋转的问题,因为该图片没有旋转信息,所以在ios低处理图片的时候,可以以正确的方向处理图片。
ios 高 的兼容处理
由于 ios高显示的是旋转后正确的方向,canvas 绘制drawImage也是正确的方向,不受旋转信息的影响。所以我们的处理方式和android、ios低不一样 “不用获取旋转信息,不进行旋转处理” 。
“不进行旋转处理”:如果我们和android或ios低一样,获取旋转信息(假如oritation:6),并且处理。会将一个显示正常、通过canvas绘制drawImage之后,反而旋转为一张方向不正确的图片。也就是说ios高不进行旋转处理。
function renderFileChangedImg(img, distImg) {
return new Promise(function (resolve) {
var mpImg = new MegaPixImage(img);
mpImg.render(distImg, {
maxWidth: 1980,
maxHeight: 1980,
}, resolve)
})
}
ios高 和 ios低的判断并实现
上面 “android和ios低的兼容处理”,“ios 高 的兼容处理” 分析过了我们兼容处理图片旋转时,只要区分 ios高和ios低就行,我这边查了好多资料,很少有解决办法,最终发现了一篇文章(https://juejin.im/post/5ec26d3c6fb9a043681f83c2)成功解决了我的问题, // 原理是:如果是原图的是一张 2 * 1 ,oritation为6的图片(逆时针旋转90deg)的图片,ios高 获取的图片尺寸 width为2,height为1,ios低和android获取的图片尺寸 width为1,height为2,实现代码如下
// 用一张特殊的图片来检测当前浏览器是否对带 EXIF 信息的图片进行回正
// 方法来源: https://github.com/blueimp/JavaScript-Load-Image
export function detectImageAutomaticRotation() {
return new Promise((resolve) => {
// 一张 2x1 的 JPEG 图片, EXIF Orientation: 6
const testAutoOrientationImageURL =
'data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' +
'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' +
'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' +
'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/x' +
'ABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAA' +
'AAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==';
let isImageAutomaticRotation;
if (isImageAutomaticRotation === undefined) {
const img = new Image();
img.onload = () => {
// 如果图片变成 1x2,说明浏览器对图片进行了回正
isImageAutomaticRotation = img.width === 1 && img.height === 2;
resolve(isImageAutomaticRotation);
};
img.src = testAutoOrientationImageURL;
} else {
resolve(isImageAutomaticRotation);
}
});
}
最终的解决办法
function renderFileChangedImg(img, distImg) {
return new Promise(function (resolve) {
// MegaPixImage.js 大家可以在网上查找,用户处理图片旋转和大小
var mpImg = new MegaPixImage(img);
// img 为用户上传的图片 ,通过exif获取图片的信息
detectImageAutomaticRotation().then(result=>{
// 如果为true,则为ios高,false为ios低和android
if(result){
mpImg.render(distImg, {
maxWidth: 1980,
maxHeight: 1980
}, resolve)
}else{
EXIF.getData(img, () => {
var allMetaData = EXIF.getAllTags(img);
// 获取图片的旋转信息
var orientation = allMetaData.Orientation;
// 旋转图片
if(orientation !== 0){
console.log('压缩并纠正旋转')
mpImg.render(distImg, {
maxWidth: 1980,
maxHeight: 1980,
orientation: orientation
}, resolve)
}else{
console.log('压缩图片尺寸')
mpImg.render(distImg, {
maxWidth: 1980,
maxHeight: 1980
}, resolve)
}
})
}
})
})
}
Foodie App内应用:
目前测试foodie android系统,通过jsBridge拍照或相册选择拿到的图片,在exif读取的时候,读取不成功,不执行回调,所以android处理方式和ios高一样,幸运的是android拍摄的照片,都纠正了方向,并且旋转 oritation 的值为0,影响范围不大,代码如下:
Handlers.detectImageAutomaticRotation().then(res=>{
// res 为 true 则浏览器对图片进行了回正,canvas操作的是纠正后的图片
if(res||!BrowserChecker.isIos()){
setTimeout(function () {
img = null;
},1000)
return sucCb(img);
}else{
EXIF.getData(img, () => {
var allMetaData = EXIF.getAllTags(img);
var orientation = allMetaData.Orientation;
var mpImg = new MegaPixImage(img);
mpImg.render($('#bridgeImg')[0], {
maxWidth: 3980,
maxHeight: 3980,
// quality: 0.6,
orientation: orientation
}, function () {
setTimeout(function () {
img = null;
},1000)
return sucCb($('#bridgeImg')[0])
});
})
}
})