新版浏览器(chrome 81+, safari 13.1+, safari ios 13.4+, firefox 26+)会自动旋转img标签中的带方向标识(有exif信息, 有orientation且不为1)的图片.
而大家常用的文件上传插件 plupload 等 仍然没有对此进行处理, 导致缩略图及resize后保存图片时会出现方向不对的问题.
关于css的image-orientation支持兼容性见: https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation#browser_compatibility
部分版本中对 background-image也进行了自动旋转
下面用一个2px*1px, 方向为6的小图来演示一下效果
<html>
<head>
<title>是否自动旋转img标签的图片</title>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" />
</head>
<body>
<script>
document.writeln("UA: "+navigator.userAgent);
</script>
<div>
<img src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QCXRXhpZgAATU0AKgAAAAgAAwESAAMAAAABAAYAAIdpAAQAAAABAAAAMoglAAQAAAABAAAANAAAAIMAAAADAAEAAgAAAAJOAAAAAAIABQAAAAMAAABeAB0AAgAAAAsAAAB2AAAAAAAAAB8AAAABAAAAMAAAAAEAABTqAAAAZDIwMjE6MDc6MjQAAAAAAE1nRXhpMDI1OAD/2wBDAAcFBQYFBAcGBgYIBwcICxILCwoKCxYPEA0SGhYbGhkWGRgcICgiHB4mHhgZIzAkJiorLS4tGyIyNTEsNSgsLSz/2wBDAQcICAsJCxULCxUsHRkdLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCz/wgARCAABAAIDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAP/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==" style="width:20px; border:1px solid #f00;" />
</div>
<div>
css设置"image-orientation: none"
</div>
<div>
<img src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QCXRXhpZgAATU0AKgAAAAgAAwESAAMAAAABAAYAAIdpAAQAAAABAAAAMoglAAQAAAABAAAANAAAAIMAAAADAAEAAgAAAAJOAAAAAAIABQAAAAMAAABeAB0AAgAAAAsAAAB2AAAAAAAAAB8AAAABAAAAMAAAAAEAABTqAAAAZDIwMjE6MDc6MjQAAAAAAE1nRXhpMDI1OAD/2wBDAAcFBQYFBAcGBgYIBwcICxILCwoKCxYPEA0SGhYbGhkWGRgcICgiHB4mHhgZIzAkJiorLS4tGyIyNTEsNSgsLSz/2wBDAQcICAsJCxULCxUsHRkdLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCz/wgARCAABAAIDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAP/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==" style="width:20px; border:1px solid #f00; image-orientation: none" />
</div>
</body>
</html>
在新版pc浏览器的效果:
在老版手机浏览器上的效果:
浏览器对<img src="" />
的处理, 其实也是走了new Image()
的. 所以检测浏览器是否自动旋转就很简单了
var _canAutoRotateImgLabel = false;//是否支持自动旋转img标签的带exif的图片
var img = new Image();
img.onload = function(){
_canAutoRotateImgLabel = img.naturalWidth == 1;//如果自动旋转了, 则宽度是原图的高度1px
}
//这是一个 width=2px, height=1px, orientation=6 的图片
img.src = "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QCXRXhpZgAATU0AKgAAAAgAAwESAAMAAAABAAYAAIdpAAQAAAABAAAAMoglAAQAAAABAAAANAAAAIMAAAADAAEAAgAAAAJOAAAAAAIABQAAAAMAAABeAB0AAgAAAAsAAAB2AAAAAAAAAB8AAAABAAAAMAAAAAEAABTqAAAAZDIwMjE6MDc6MjQAAAAAAE1nRXhpMDI1OAD/2wBDAAcFBQYFBAcGBgYIBwcICxILCwoKCxYPEA0SGhYbGhkWGRgcICgiHB4mHhgZIzAkJiorLS4tGyIyNTEsNSgsLSz/2wBDAQcICAsJCxULCxUsHRkdLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCz/wgARCAABAAIDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAP/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==";
也可以根据浏览器版本号进行判断, 但是在用chrome模拟device时可能会因为指定的UA版本过低导致误判
var _canAutoRotateImgLabel = false;
var browser_name = Env.browser.toLowerCase();
if(
(browser_name.indexOf('safari') > -1 && parseFloat(Env.version) >= 13.4)
|| (browser_name.indexOf('chrome') > -1 && parseFloat(Env.version) >= 81)
){
_canAutoRotateImgLabel = true;
}
对于plupload修正的方法就是: 在moxie.js
的function HTML5Image()
中增加是否会自动旋转img标签的变量, 并在其resize: function(rect, ratio, options)
中增加对这个变量的判断和修正.
function HTML5Image() {
var me = this
, _img, _imgInfo, _canvas, _binStr, _blob
, _modified = false // is set true whenever image is modified
, _preserveHeaders = true
;
var _canAutoRotateImgLabel = false;//是否支持自动旋转img标签的带exif的图片. 低版本浏览器不会自动旋转, 需要修正图像.
function getAutoRotateAble() {
//使用浏览器版本号判断, 速度快, 但是pc上模拟时会因为UA导致判断错误
// var browser_name = Env.browser.toLowerCase();
// if(
// (browser_name.indexOf('safari') > -1 && parseFloat(Env.version) >= 13.4)
// || (browser_name.indexOf('chrome') > -1 && parseFloat(Env.version) >= 81)
// ){
// _canAutoRotateImgLabel = true;
// }
//用img加载一个方向为6,大小为2px*1px的图片, 然后取其宽度来判断
var img = new Image();
img.onload = function(){
_canAutoRotateImgLabel = img.naturalWidth == 1;//如果自动旋转了, 则宽度是原图的高度1px
console.log('_canAutoRotateImgLabel: ', _canAutoRotateImgLabel);
}
img.src = "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QCXRXhpZgAATU0AKgAAAAgAAwESAAMAAAABAAYAAIdpAAQAAAABAAAAMoglAAQAAAABAAAANAAAAIMAAAADAAEAAgAAAAJOAAAAAAIABQAAAAMAAABeAB0AAgAAAAsAAAB2AAAAAAAAAB8AAAABAAAAMAAAAAEAABTqAAAAZDIwMjE6MDc6MjQAAAAAAE1nRXhpMDI1OAD/2wBDAAcFBQYFBAcGBgYIBwcICxILCwoKCxYPEA0SGhYbGhkWGRgcICgiHB4mHhgZIzAkJiorLS4tGyIyNTEsNSgsLSz/2wBDAQcICAsJCxULCxUsHRkdLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCz/wgARCAABAAIDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAP/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==";// width=2px, height=1px, orientation=6
}
getAutoRotateAble();
Basic.extend(this, {
//以下都不变
resize: function(rect, ratio, options) {//这里才是最终的修改尺寸的操作
var canvas = document.createElement('canvas');
canvas.width = rect.width;
canvas.height = rect.height;
canvas.getContext("2d").drawImage(_getImg(), rect.x, rect.y, rect.width, rect.height, 0, 0, canvas.width, canvas.height);
_canvas = ResizerCanvas.scale(canvas, ratio);
_preserveHeaders = options.preserveHeaders;
// rotate if required, according to orientation tag
var orientation = (this.meta && this.meta.tiff && this.meta.tiff.Orientation) || 1;
if (!_preserveHeaders &&!_canAutoRotateImgLabel && orientation != 1) {
_canvas = _rotateToOrientaion(_canvas, orientation);
}
if( _preserveHeaders && _canAutoRotateImgLabel
&& Basic.inArray(orientation, [3,6,8]) > -1 //暂时只修正这些 todo
){
//如果保留header且浏览器会自动旋转, 则反旋转
var orientation2;
switch (orientation) {
case 3:
orientation2 = 3;
break;
case 6:
orientation2 = 8;
break;
case 8:
orientation2 = 6;
break;
}
_canvas = _rotateToOrientaion(_canvas, orientation2);
}
this.width = _canvas.width;
this.height = _canvas.height;
_modified = true;
this.trigger('Resize');
},
注意: 生成预览缩略图的代码中, 不要暴力header, 否则因为多旋转了一次导致缩略图方向不对.
function plupload_previewImage(file, callback) {//file为plupload事件监听函数参数中的file对象,callback为预览图片准备完成的回调函数
if (!file || !/image\//.test(file.type)) return ''; //确保文件是图片
if (file.type == 'image/gif') {//gif使用FileReader进行预览,因为moxie.image.Image只支持jpg和png
var fr = new moxie.file.FileReader();
fr.onload = function () {
callback(fr.result);
fr.destroy();
fr = null;
}
fr.readAsDataURL(file.getSource());
}
else{
var preloader = new moxie.image.Image();
preloader.onload = function () {
preloader.resize({"width":300, "height":300, "preserveHeaders":false});//先压缩一下要预览的图片: 宽,高,不保留header(会自动旋转). 这里必须设置false
var imgsrc = preloader.type == 'image/jpeg' ? preloader.getAsDataURL('image/jpeg', 80) : preloader.getAsDataURL(); //得到图片src,实质为一个base64编码的数据 => 这里会丢失exif信息(canvas.toDataURL导致的)
callback && callback(imgsrc); //callback传入的参数为预览图片的url
preloader.destroy();
preloader = null;
};
preloader.load(file.getSource());
}
}