最近Webアプリを作成したときにググった、画像表示についての備忘録です。
実現したかったのは2つ。
- アップロード画像を指定領域(px)内いっぱいに表示(プレビュー)
- iPhone/iPad で発生する画像の傾きを修正(EXIF情報とどう向き合うか)
最低限のHTML
<!DOCTYPE html>
<html lang="ja">
<head>
<title>タイトル</title>
<meta charset="UTF-8">
<script type="text/javascript" src="js/jquery-1.12.1.min.js" ></script>
<script type="text/javascript" src="js/exif.js" ></script>
<script type="text/javascript" src="js/megapix-image.js" ></script>
<script type="text/javascript" src="js/imgLiquid-min.js" ></script>
<script type="text/javascript" src="js/select-image.js" ></script>
</head>
<body>
<form enctype="multipart/form-data">
<input id="file-btn" type="file" name="image" accept="image/*">
</form>
<!-- プレビュー表示 -->
<p class="image-box" style="width:300px; height:200px;">
<img src="" alt=""/>
</p>
</body>
</html>
1. アップロード画像を指定領域内いっぱいに表示
ローカルにあるファイルを選択してブラウザに表示させるには、File API と FileReader APIが便利です。
参考;JavaScript で File API を使用してファイルを読み取る
指定領域内に画像を表示させるときは、imgLiquid が一番楽そうでした。
imgLiquidの使い方は、こちらやこちらを参考にしてみてください。
画像プレビュー
$(function(){
$('#file-btn').change(function(){
if (!this.files.length) {
return;
}
var file = this.files[0], //画像1つのみ選択
image = $('.image-box'),
fileReader = new FileReader();
// 読み込みが完了した際のイベントハンドラ。imgのsrcにデータセット
fileReader.onload = function(event) {
// 読み込んだデータをimgに設定
image.children('img').attr('src', event.target.result);
// imgLiquid - imgの親要素に指定
image.imgLiquid();
};
// 画像読み込み
fileReader.readAsDataURL(file);
});
});
これでどんな画像のサイズでも、.image-box
に指定したサイズ内いっぱい(300×200)に画像が表示されるはずです。
※分かりやすいよう赤枠で囲みました
2. iPhone/iPad 使用時に発生する画像の傾きを修正
スマートフォンではファイルを選択
をタップすると、カメラを直接起動して撮ったものを反映することも出来ます。
が、iPhone/iPadで撮影したものはすべて横向きで表示されてしまいます。
アップロードするのみであればPHP等で処理してもいいのですが、今回はプレビューでも表示させたかったのでそのあたりもJSでどうにかします。
exif-js
画像のEXIF情報にある撮影方向の向きを調べます。exif-jsが便利です。
---
var file = this.files[0], //画像1つのみ選択
image = $('.image-box img'),
fileReader = new FileReader();
/* 追加 ここから */
var orientation = 0;
EXIF.getData(file, function(){
orientation = file.exifdata.Orientation;
if(orientation === undefined){
orientation = 1;
}
});
/* 追加 ここまで */
// 読み込みが完了した際のイベントハンドラ。imgのsrcにデータセット
fileReader.onload = function(event) {
---
orientation
には1から8までの数字が入ってきます。
どの数字がどんな意味を表すかは、下の表を参考にしてください。
Orientation | 修正方法 |
---|---|
1 | そのまま |
2 | 上下反転 |
3 | 180度回転 |
4 | 左右反転 |
5 | 上下反転、時計周りに270度回転 |
6 | 時計周りに90度回転 |
7 | 上下反転、時計周りに90度回転 |
8 | 時計周りに270度回転 |
ごまかしたところ
たまにorientation
が上手く取れないことがあるそうなので、そのときは強制的に1(そのまま)にしました
修正方法が分かったものの、自分でcanvas使って回転させて…なんてことする気力はないので、ios-imagefile-megapixelを使います(iOSとか入ってますが特に意味はないそうです)
select-image.jsを編集します
---
// 読み込みが完了した際のイベントハンドラ。imgのsrcにデータセット
fileReader.onload = function(event) {
// 読み込んだデータをimgに設定
// image.attr('src', event.target.result); // 削除
/* 追加 ここから */
var mpImg = new MegaPixImage(file);
mpImg.render(image.children('img')[0], { orientation: orientation });
/* 追加 ここまで */
// imgLiquid - imgの親要素に指定
image.parent().imgLiquid();
};
---
これでOK!と思ったのですが、imgLiquid
が上手く効いてくれなかったので、荒業ですが megapix-image.js
の236行目辺りを編集します。
---
var tagName = target.tagName.toLowerCase();
if (tagName === 'img') {
target.src = renderImageToDataURL(this.srcImage, opt, doSquash);
/* 追加 ここから */
target.parentNode.style.backgroundImage = 'url(' + target.getAttribute("src") + ')';
/* 追加 ここまで */
} else if (tagName === 'canvas') {
renderImageToCanvas(this.srcImage, target, opt, doSquash);
}
if (typeof this.onrender === 'function') {
---
imgLiquid
では、imgタグの画像を指定したセレクタ(.image-box)のスタイルに当てることで、指定領域いっぱいに表示していました。CSSはこんな感じです。
background-size: cover;
background-position: center center;
background-repeat: no-repeat;
background-image: url(imgの画像);
megapix-image.js
を使うと、imgタグから画像パスの取得が上手くいかないみたいなので、.image-box
の background-image
に直接、回転を修正した画像があたるように変更しました。
まとめ
コードは汚いですが実現したいものは一応出来たので、これでよしとします。
ツッコミ大歓迎です!