JPEG画像は画像の回転角度(元の画像から何度回転したか)をEXIF情報内のOrientationに保持している。
この回転角度を意識せずにWEBアプリでアップロードする画像の描画等を行うと、想定とは異なる向きの画像が表示されたりする。
今回は、キャンバスの背景画像に回転させた画像を設定していく。
html上のimgタグで回転表示させるだけであれば、transformに適切な角度を設定するだけで上手く表示できるが、キャンバス上の場合、それだけでは上手く表示ができない。
適切な個所に表示させるためには、回転角度に応じて、originの設定を変更してから回転させる必要がある。
HTML(画像読込フォーム)
画像ファイルはフォームから読み込んだものを設定する。
<form>
<!-- 画像読込フォーム -->
<input type="file" id="selectImage" name="selectImage" accept="image/*" >
</form>
<!-- キャンバス -->
<canvas class="imageCanvas" id="imageCanvas"></canvas>
htmlについては、一先ず、画像読込部分とキャンバス部分のみ記載する。
JavaScript部分は以下の通り。
画像読込開始時の処理
/*
* ボタンからの画像ファイルの読込
*/
document.getElementById('selectImage')
.addEventListener('change',async function (e) {
//画像ファイル用変数
let imgFile = e.target.files[0];
//画像ファイル選択判定
if (imgFile != null ) {
drawBackgroundImage(imgFile);
}else {
});
画像選択時に実行される処理。メイン処理と分けたのは、ネストが深くなるのを避けるため。
キャンバス描画メイン処理
/*
* drawBackgroundImage()
* キャンバスの背景に画像を描画する。
*/
async function drawBackgroundImage(imgFile) {
//フォームから読み込んだ画像データをキャンバスの背景に描画する。
//exif情報の取得
let orientation = await getOrientation(imgFile);
console.log("orientation:", orientation);
//回転角度の設定
let imgAngle = setAngle(orientation);
//画像からblobUrlを作成し、背景画像として表示する。
let blobUrl = window.URL.createObjectURL(imgFile);
//blobURLから画像データ読込
let imgElement = await loadImage(blobUrl).catch(e => {
console.log("onload error", e);
});
//画像の縦横サイズ取得
let imgSize = {
"imgHeight": 0, //初期化
"imgWidth": 0 //初期化
}
imgSize = getImgSize(imgElement, imgAngle);
//キャンバスのサイズを画像のサイズに変更する。
canvas.setWidth(imgSize.imgWidth);
canvas.setHeight(imgSize.imgHeight);
//画像の原点の設定
let imgOrigin = {
"imgOriginX": "", //初期化
"imgOriginY": "" //初期化
}
imgOrigin = getImgOrigin(imgAngle);
//キャンバスの背景に画像描画
canvas.setBackgroundImage(blobUrl, canvas.renderAll.bind(canvas), {
backgroundImageOpacity: 0,
backgroundImageStretch: false,
originX: imgOrigin.imgOriginX,
originY: imgOrigin.imgOriginY,
angle: imgAngle
});
console.log("drawBackgroundImage end");
}
EXIF情報の取得、回転角度の設定、画像データの読込、画像に合わせたキャンバスサイズの設定、背景画像の原点設定、キャンバスに背景描画と処理を行っていく。
フォームからの画像読込やexif情報の取得は非同期で行われるため、Promiseとasync/awaitを使う。
EXIF情報の取得
/*
* getOrientation
* 戻り値
* resolve: exifdata.Orientationを返す
* reject: なし
*/
function getOrientation(imgFile){
return new Promise( (resolve, reject) => {
EXIF.getData(imgFile, function() {
console.log(imgFile.exifdata);
resolve(imgFile.exifdata.Orientation);
});
//EXIFデータの取得完了時以外は何も返さない。
});
}
Promiseでresolveのみ返すようにする。
回転角度の設定
/*
* setAngle
* orientationの値に応じて回転角度を返却する。
* 引数
* orientation: exif.Orientation
* 戻り値
* imgAngle: 正しい向きに戻すための回転角度(時計回り)
*/
function setAngle(orientation){
// orientationの値に応じて回転角度を返却する。
switch(orientation){
case 1: //回転なし
imgAngle = 0;
break;
case 3: //元画像を180度回転している。
imgAngle = 180;
break;
case 6: //元画像を270度回転している。
imgAngle = 90;
break;
case 8: //元画像を90度回転している。
imgAngle = 270;
break;
default:
imgAngle = 0;
break;
}
return imgAngle;
}
orientationの値に応じて、回転させる角度を返す。
画像データ読込
/*
* 画像読込時用のPromiseを返す関数
* 戻り値
* resolve:画像データ
* reject :error情報
*/
function loadImage(blobImg){
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = (e) => reject(e);
img.src = blobImg;
});
}
画像を読み込むと、画像サイズを取得できるため、キャンバスサイズを設定していく。
画像サイズの取得
/*
* 画像のサイズを取得する。
*/
function getImgSize(imgElement, imgAngle){
//変数宣言と初期化
let imgSize = {
"imgHeight": 0,
"imgWidth": 0
}
//画像の回転角度に合わせて、画像の縦横を取得する。
if ((imgAngle === 90) || (imgAngle === 270)) {
console.log("convert height and width");
imgSize.imgHeight = imgElement.naturalWidth;
imgSize.imgWidth = imgElement.naturalHeight;
} else {
imgSize.imgHeight = imgElement.naturalHeight;
imgSize.imgWidth = imgElement.naturalWidth;
}
return imgSize;
}
回転角度が90度、270度の場合、回転後に縦と横が入れ替わるので、それを考慮してキャンバスサイズを設定する。
背景画像の原点設定
/*
* 画像の回転角度に合わせてOriginX、OriginYを設定する。
*/
function getImgOrigin(imgAngle){
//変数宣言と初期化
let imgOrigin = {
"imgOriginX": "",
"imgOriginY": ""
}
//回転角度に応じてOriginを設定する。
switch(imgAngle){
case 0:
imgOrigin.imgOriginX = "left";
imgOrigin.imgOriginY = "top";
break;
case 90: //左下を起点に回転させる
imgOrigin.imgOriginX = "left";
imgOrigin.imgOriginY = "bottom";
break;
case 180: //右下を起点に回転させる
imgOrigin.imgOriginX = "right";
imgOrigin.imgOriginY = "bottom";
break;
case 270: //右上を起点に回転させる
imgOrigin.imgOriginX = "right";
imgOrigin.imgOriginY = "top";
break;
default:
imgOrigin.imgOriginX = "left";
imgOrigin.imgOriginY = "top";
break;
}
return imgOrigin;
}
回転させる時の原点の設定を誤ると、回転後にキャンバス上に画像が見えないということが起こる。
imgタグだとこんなことを考慮しなくて良いのだが、キャンバスの場合、考慮する必要がある。
WEBサーバにアップロードしてから画面に表示でもOKな場合、サーバ側で回転させて、キャンバス上は普通に表示するのが楽だと思う。
そもそも、画像を色々加工して表示したいとか、理由がない限り、画像描画にキャンバスは使う必要がない。というか、面倒なだけw