FileAPIプレビューのOrientation対応

imatomix
2017年12月3日 5:37

経緯

  • スマートフォンで撮影した画像をFileAPIでプレビューした場合、撮影時のスマートフォンの上下の向きでWEB上での表示で向きが変わることがある。
  • ブラウザやデバイスによってまちまちだったりする。

ゴール

  • 撮影時通りの意図した方向でプレビュー表示させる。

調査

  • 画像はexif情報を持っていて、スマートフォンではexif情報内のOrientationをみて表示する側が向きを調整しているとのこと。
  • Orientationの種類と対応番号は以下の通り
  1. 通常
  2. 1.を左右反転
  3. 1.を180度回転
  4. 2.を180度回転
  5. 2.を270度回転
  6. 1.を270度回転
  7. 2.を90度回転
  8. 1.を90度回転

スクリプト

プレビュー画像の回転

  • event.target.result で得る Base64Data を直接回転はできない?よう。
  • 一度 canvas と context を作って変形処理を施し、そこにdrawImageしたものをcanvas.toDataURL()で返す。

全体

FilePreview
// ファイルに変更があったときに呼び出す function(e){ var images = e.target.files // ファイルを全て取得 var formData = new FormData for(var image of images){ if(!image.type.match('image.*')) { cotinue // 画像以外は無視 } formData.append('image', image) // formDataに追加 var reader = new FileReader() reader.onload = (function(theFile){ return function(e){ exif.getData(theFile, function(){ rotateBase64Image(e.target.result, theFile.exifdata.Orientation, function(url){ // src=url で、あとはお好みの形でDOMに追加 }) }) } })(image) reader.readAsDataURL(image) } } function rotateBase64Image(base64Data, orientation, callback){ /* canvasを作って、サイズとcontextをorientation に合わせてゴニョゴニョして、そこに画像を描画したイメージの data: URL でコールバック */ var canvas = document.createElement('canvas') var context var image = new Image() image.onload = function(){ switch (orientation) { case 2: // 左右反転 canvas.height = image.height canvas.width = image.width context = canvas.getContext('2d') context.scale(-1, 1) context.translate(-image.width, 0) break; case 3: // 180度回転 canvas.height = image.height canvas.width = image.width context = canvas.getContext('2d') context.translate(canvas.width, canvas.height) context.rotate(Math.PI) break; case 4: // 上下反転 canvas.height = image.height canvas.width = image.width context = canvas.getContext('2d') context.scale(1,-1) context.translate(0, -canvas.height) break; case 5: // 上下反転から右90度回転 canvas.height = image.width canvas.width = image.height context = canvas.getContext('2d') context.scale(1, -1) context.rotate(-Math.PI/2) break; case 6: // 左90度回転 canvas.height = image.width canvas.width = image.height context = canvas.getContext('2d') context.rotate(Math.PI/2); context.translate(0, -canvas.width) break; case 7: // 左右反転から右90度回転 canvas.height = image.width canvas.width = image.height context = canvas.getContext('2d') context.scale(-1, 1) context.translate(-canvas.width, canvas.height) context.rotate(-Math.PI/2) break; case 8: // 右90度回転 canvas.height = image.width canvas.width = image.height context = canvas.getContext('2d') context.rotate(-Math.PI/2); context.translate(-canvas.height, 0) break; default: // そのままを返す callback(base64Data) return break; } context.drawImage(image, 0, 0, image.width, image.height); callback(canvas.toDataURL()) } image.src = base64Data }

感想

  • おもったより面倒くさい。もっといいやり方あるのかも。
  • そのうちブラウザ側で全部対応してくれるようになるんだろうなぁ。