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