Help us understand the problem. What is going on with this article?

アップロード画像のプレビュー表示をキレイにする

More than 3 years have passed since last update.

最近Webアプリを作成したときにググった、画像表示についての備忘録です。
実現したかったのは2つ。

  1. アップロード画像を指定領域(px)内いっぱいに表示(プレビュー)
  2. iPhone/iPad で発生する画像の傾きを修正(EXIF情報とどう向き合うか)

最低限のHTML

index.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の使い方は、こちらこちらを参考にしてみてください。

画像プレビュー

select-image.js
$(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)に画像が表示されるはずです。
※分かりやすいよう赤枠で囲みました

https://gyazo.com/d266dbf52489d4e2eb560141afec6453

2. iPhone/iPad 使用時に発生する画像の傾きを修正

スマートフォンではファイルを選択をタップすると、カメラを直接起動して撮ったものを反映することも出来ます。
が、iPhone/iPadで撮影したものはすべて横向きで表示されてしまいます。

アップロードするのみであればPHP等で処理してもいいのですが、今回はプレビューでも表示させたかったのでそのあたりもJSでどうにかします。

exif-js

画像のEXIF情報にある撮影方向の向きを調べます。exif-jsが便利です。

select-image.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を編集します

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行目辺りを編集します。

megapix-image.js
---

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-boxbackground-image に直接、回転を修正した画像があたるように変更しました。

まとめ

コードは汚いですが実現したいものは一応出来たので、これでよしとします。
ツッコミ大歓迎です!

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away