目录
问题描述
了解手机拍摄方向
关于照片的 EXIF 信息
html 如何加载图片
遇到这种问题我们如何解决?
解决思路
exif.JS 的使用
1. 先引用 JS 文件
2. 使用方法获得对应 img 图片的 exif 信息
3. 根据 Orientation 的值不为 6, 判断需要进行旋转, 使用 CSS 进行相应的
旋转 exif.JS 原理思路
一些专有名词?
- ArrayBuffer?
- DataView?
问题描述
这个问题之所以不常发现, 是因为我们很少加载手机拍摄的图片, 更少加载图片被人为旋转过的图片.
在我们加载图片的时候, 浏览器有时候加载带有旋转信息的图像文件的时候方向有旋转的偏差, 下面就是深度剖析一下这个问题和解决方案.
了解手机拍摄方向
一张图带你了解手机拍摄方向和在手机系统中呈现的样式.
手机拍摄方向. PNG
这些照片拍出来, 不管呈现是不是正的, EXIF 里面的旋转信息都为 1, 表示正方向.
可以看出来,
如果是垂直方向拍出来的, 会根据横向纵向进行方向的调整.
如果是水平方向拍出来的, 因为无法判断手机的方向, 所以拍的总是按照横短竖长为正方向.
关于照片的 EXIF 信息
Exif 就是用来记录拍摄图像时的各种信息:
图像信息 (厂商, 分辨率等)
相机拍摄记录 (ISO, 白平衡, 饱和度, 锐度等)
缩略图 (缩略图宽度, 高度等)
gps(拍摄时的经度, 纬度, 高度) 等
将这些信息按照 JPEG 文件标准放在图像文件二进制流的头部.
HTML 如何加载图片
那么一个图片如何加载的呢? 上面的图片使用了 < div>+backgroundImage 和 < img>+src 两种方式渲染.
浏览器正常加载图片. PNG
可以看出来, 渲染的效果和手机刚拍摄的展示的效果是一样的.
那么我们现在, 点击图片右键将图片进行旋转. 再看效果.
向右旋转. PNG
第一张照片顺时针旋转一次
第二张照片顺时针旋转两次
第三张照片顺时针旋转一次
第四张照片逆时针旋转一次
旋转之后的图片方向. PNG
这个时候,
发现加载的方向依旧和没有旋转过是一样的.
(为了避免是没刷新啥的, 我还修改了一下样式)
旋转之后没有任何变化. PNG
然后我们再将图片使用图片工具打开, 进行旋转保存, 查看效果.
使用照片编辑器编辑图片. PNG
使用画板编辑图片. PNG
然后在浏览器展示看, 发现浏览器加载的图片进行了旋转
旋转之后没有任何变化. PNG
可以看出来, 在 HTML 进行加载的时候, 根据不同的渲染方式, 会有不一样的展示. 原因是什么呢?
使用电脑右键的旋转或者手机直接旋转
只是更改了图像附加的 EXIF 的一些旋转信息, 本身图片的元数据并没有进行改变.
使用图画工具或者照片编辑器进行旋转保存
会直接更改图片的元数据, 并将当前的 EXIF 旋转信息置为正方向.
浏览器会根据图片本身的元数据进行渲染. 会忽略 EXIF 里面的旋转信息 Orientation, 永远展示的是 Orientation 为 1 的方向.
遇到这种问题我们如何解决?
解决思路
只要我们可以获取到图片本身的信息, 就可以对其方向进行判断了. 但是本身文件并没有给我们暴露那些接口获取信息, 使用原生方法比较复杂. 还好的是有一个插件 exif.JS 解决了这个问题.
exif.JS 插件 API
exif.JS 的使用
1. 先引用 JS 文件
<script src="exif.js"></script>
2. 使用方法获得对应 img 图片的 exif 信息
- EXIF.getData(document.getElementById('img'), function(){
- EXIF.getAllTags(this); // 获取所有信息, 以对象形式返回
- EXIF.getTag(this, 'Orientation') // 获取图像的 Orientation 指定属性值
- EXIF.pretty(this); // 获取所有信息, 以字符串形式返回
- });
3. 根据 Orientation 的值不为 6, 判断需要进行旋转, 使用 CSS 进行相应的旋转
根据值的不同做不同的旋转
可供参考的 exif-orientation 说明
exif-orientation 说明. PNG
参数 | 0 行(未旋转上) | 0 列(未旋转左) | 旋转(方法很多) |
---|---|---|---|
1 | 上 | 左 | 0° |
2 | 上 | 右 | 水平翻转 |
3 | 下 | 右 | 180° |
4 | 下 | 左 | 垂直翻转 |
5 | 左 | 上 | 顺时针 90°+ 水平翻转 |
6 | 右 | 上 | 顺时针 90° |
7 | 右 | 下 | 顺时针 90°+ 垂直翻转 |
8 | 左 | 下 | 逆时针 90° |
exif.JS 原理思路
首先定义了一套 16 进制数据对应的种类标记, 已经种类对应的值的含义
EXIF tags.PNG
根据图片的加载途径不同, 殊途同归的转化成 ArrayBuffer 对象, 并创建一个 DataView 对象进行操作.
exif 流程图. PNG
根据一套规则去解析 ArrayBuffer 对象
先判断其是不是 jpeg 图片文件
(照片只存储 jpg 格式, 不存储 PNG 格式, 所以不需要进行 PNG 文件的考虑)
二进制文件通过文件头识别文件类型
jpeg 的头文件标识是 (0xFF, 0xD8), 这是 JPEG 协议规定的
用 16 进制转化为 10 进制为 (255,216), 如果是 jpeg 文件我们就进行下一步解析.
二进制文件类型. PNG
然后找到对应的 Exif 字母标记, 找到开始读取信息
匹配上面的键值对, 进行配对输出.
最终输出 exif 对象. PNG
一些专有名词?
- ArrayBuffer?
- ArrayBuffer MDN
arrayBuffer.PNG
ArrayBuffer 对象 用来表示通用的, 固定长度的原始二进制数据缓冲区.
根据不同的类型进行区分
Uint8Array 是无符号八进制数组
Int8Array 是有符号八进制数组
Int16Array 是有符号 16 进制数组.
其中 ArrayBuffer 对象不能直接操作, 原型链上也没有暴露的方法, 所有只能通过类型数组对象或 DataView 对象来操作, 它们会将缓冲区中的数据表示为特定的格式, 并通过这些格式来读写缓冲区的内容.
- DataView?
- DataView MDN
dataView.PNG
DataView 对象用于呈现指定的缓存区数据, 可以认为是一个二进制解释器, 下面的不同的方法, 就是根据不同的位数去取数据进行解析.
- var buffer = new ArrayBuffer(16);
- var dv = new DataView(buffer);
- dv.setInt16(0, 42);
- dv.getInt16(0); //42
来源: http://www.jianshu.com/p/97d517ba9145