2017-12-3 22:37
fileAPI

FileAPIプレビューのOrientation対応

経緯

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

ゴール

  • 意図した方向でプレビュー表示させる

調査

  • 画像はexif情報を持っていて、スマートフォンではexif情報内のOrientationをみて表示する側が向きを調整しているとのこと。
  • Orientationの種類と対応番号は以下の通り image

スクリプト

プレビュー画像の回転

  • 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 }

感想

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