Exif.js Exif に設定されている回転情報に対応したinput要素(画像)のプレビュー
どうも藤原です。今年のGWは神がかってますね。弊社は5月1日が創立記念日ですので、
4月最終週は (^-^)です。
本日は 画像の Exif 情報を取得する Exif.js をご紹介いたします。
背景
input 要素に画像を添付した時に FileReader を使ってプレビュー表示していたんですが、 スマホで行うと撮影した画像とプレビューしてる画像の回転があってないことありますよね。 原因はExif (wiki)の回転情報です。 PHPで回転を戻して保存したりすることはあったのですが、JSでプレビューしているときも(POST前)正しく表示したいということです。
Exif.js
抜き出すには結構手間な作業必要なのですが、そんな時見つけたのがExif.jsです。 これで簡単に回転を取得できるようになります。
サンプル
htmlは以下で、プレビュー自体の実装方法は input の label に対して backgrount-imageで指定してあげます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<style>
label {
display: block;
width: 250px;
height: 250px;
background-size: contain;
background-repeat: no-repeat;
}
</style>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- exif-js.js の読み込み -->
<script src="/common/js/exif-js.js"></script>
</head>
<body>
<form action="" enctype="multipart/form-data">
<input type="file" name="image1" class="file" accept="image/*" id="image1">
<label for="image1"></label>
</form>
<script src="/common/js/exif.js"></script>
<script src="/common/js/preview.js"></script>
</body>
</html>
preview.js (jQuery)
(function (window, $) {
'use strict';
function base64ToArrayBuffer(base64) {
base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
var binaryString = atob(base64);
var len = binaryString.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
$(function () {
var $input = $(this).find('input');
var $label = $(this).find('label');
$input.on('change', function () {
if (this.files && this.files[0]) {
var reader = new FileReader()
reader.onload = function (e) {
/** 画像の指定自体は以下のみ **/
$label
.css({
"background-image": 'url(' + e.target.result + ')'
});
//回転対応 , 回転具合を見てlabelを回転
var arrayBuffer = base64ToArrayBuffer(this.result);
var exif = EXIF.readFromBinaryFile(arrayBuffer);
var rotate = 0;
if (exif && exif.Orientation) {
switch (exif.Orientation) {
case 3:
rotate = 180;
break;
case 6:
rotate = 90;
break;
case 8:
rotate = -90;
break;
}
$label.css({
"transform": "rotate(" + rotate + "deg)",
"-webkit-transform": "rotate(" + rotate + "deg)"
});
}
//回転対応 , 回転具合を見てlabelを回転
}
reader.readAsDataURL(this.files[0]);
}
});
});
})(window, jQuery);
preview.js(ES6)
const input = document.getElementById('image1');
const label = document.querySelector('label');
const base64ToArrayBuffer = (base64) => {
base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
const binaryString = atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
let reader = null;
input.addEventListener('change', () => {
if (input.files && input.files[0]) {
reader = new FileReader();
reader.onload = (e) => {
label.style.backgroundImage = `url(${e.target.result})`;
//回転対応 , 回転具合を見てlabelを回転
const arrayBuffer = base64ToArrayBuffer(reader.result);
const exif = EXIF.readFromBinaryFile(arrayBuffer);
let rotate = 0;
if (exif && exif.Orientation) {
switch (exif.Orientation) {
case 3:
rotate = 180;
break;
case 6:
rotate = 90;
break;
case 8:
rotate = -90;
break;
}
}
label.style.transform = `rotate(${rotate}deg)`;
label.style.webkitTransform = `rotate(${rotate}deg)`;
}
reader.readAsDataURL(input.files[0]);
}
});
ポイント
Exif.jsに読みこませる前に base64ToArrayBuffer という関数で画像データを変換してあげるところですね。
また回転については Exif Orientation Tag
を参考に回転させています。(インカメラでとっても反転が無かったので switch には反転用の分岐を今回入れませんでした。必要な場合は
反転時に style の scale を使用して反転する必要があります。)
まとめ
いかがでしたでしょうか?意外と簡単だと思いますので、是非お試しくください。 (良しなに回転させて表示するのが、ユーザーにとって必ずしも正ではないので、使用する場合はご注意ください。)