web関連

【HTML5・javascript】canvasで読み込んだ画像を親要素の幅に合わせ高さを調整する方法

canvasを使って読み込んだ画像が親要素の幅に合わせて画像の比率を保ったままいい感じにできないかなぁって思い調べた時の備忘録

参考:ソース周り丸々参考にした

ほぼソースコードについてはここのサイトの使わせてもらった
ただ、めちゃくちゃ参考になったんだけど親要素の高さをCSSのpadding-topで固定しているね
そうじゃなくて親の幅を取得して画像の比率にうまいこと合わせたかったので一部分修正した

それと、どこがどういう処理しているのか勉強がてら調べた

ソースコード

勉強がてらes6で書き直したりしてる

まずはデモ
ブラウザ幅に対して50%でレスポンシブに対応させてる
ブラウザ幅を狭めたりしてみてね
<div id="canvas-container">
    <canvas id="canvas"></canvas>
</div>

<script>
const parent = document.getElementById('canvas-container');
const canvas = document.getElementById('canvas');
// 2次元の描画を行うメソッド
const ctx = canvas.getContext('2d');
// リサイズ時の判定用変数
let isInit = false;
// img要素を生成
const img = new Image();
// 読み込み用の画像をimg要素に挿入
img.src = "https://source.unsplash.com/xGy_DKmPYEk/";

// 描画用関数
let render = () => {
    // 比率格納用変数
    let scale = 0;
    if(isInit){// ブラウザをリサイズ時したときの処理
        // canvasを再描画するために既存の画像を消す
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    }else{
        isInit = true;
    }
    // 画像の元のサイズ取得
    let img_w = img.naturalWidth;
    let img_h = img.naturalHeight;
    // 親の幅を取得
    let parent_w = parent.clientWidth;
    // 親の幅をcanvasに反映
    canvas.width = parent_w;
    // 横幅の比率から画像の高さを割り出す
    canvas.height = img_h * (parent_w / img_w);
    // 比率を求める
    scale =  canvas.width / img.width;
    // 比率を元に描画される画像サイズを調整
    ctx.setTransform(scale, 0, 0, scale, 0, 0);
    // canvasに描画
    ctx.drawImage(img, 0, 0);
}

// リサイズ用関数
let main = () => {
    img.addEventListener('load', render, false);
    window.addEventListener('resize', render, false);
}

main();
</script>

一度描画したコンテンツを消すのにclearRectを使うみたいね

clearRect(x座標, y座標, width, height)

setTransform()でcanvas内に描画する画像のサイズを調整してる
今回のソースコードでcanvas自体はブラウザ幅に合わせてリサイズされるけど、中に描画される画像自体はリサイズされないからこのメソッドで調整する必要性があるみたい

// 初期値:setTransform(1, 0, 0, 1, 0, 0)
setTransform(伸縮x, 傾斜y, 傾斜x, 伸縮y, 移動x, 移動y)

参考:比率計算

比率の計算は、
[元の高さ]×([今の横幅]÷[元の横幅])で計算できるみたい

640×480pxの画像があって、親の幅が320pxだったら

[元の高さ]×([今の横幅]÷[元の横幅])
480 × (320 ÷ 640) = 240

だから合ってるね、高さはこれで調整した

メモ:addEventListenerについて

addEventListener()あんまり使ったことなかった

// addEventListener(イベント, 関数, オプション);
addEventListener('load', render, false);

上記の書き方だと、ロードしたらrender関数を実行するって処理みたいね

オプションのfalseで子要素から親要素にイベント伝播して、親要素のイベントを先に拾うときはtrueにすればいいみたい

メモ:drawImage()でサイズの調整できない?

drawImage()は画像を描画するためのメソッド
最低限の引数で設定を行おうとすると

// フル:drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
// 最低限:drawImage(image, dx(x座標), dy(y座標)
drawImage(img, 0, 0)

drawImage()は引数めっちゃあるんだけど、引数の意味よくわかんなかったから実際に触って感じたことまとめておく

image img要素、canvas要素、video要素を指定できる
形としては<img src="画像URL">を引数に指定する必要があるっぽいね
sx 画像上のx座標(基本0にする)
0以外を指定するとその数値分トリミングされちゃうね
sy 画像上のy座標(基本0にする)
0以外を指定するとその数値分トリミングされちゃうね
sw 画像の幅(初期値は画像本来の幅)
画像本来のサイズより数値を小さくするとsx、syで指定した位置から指定した数値分をトリミングして表示、大きくするとその数値分余白が一緒に出力される
sh 画像の高さ(初期値は画像本来の高さ)
画像本来のサイズより数値を小さくするとsx、syで指定した位置から指定した数値分をトリミングして表示、大きくするとその数値分余白が一緒に出力される
dx 描画するx座標(純粋なcanvas上の座標)
dy 描画するy座標(純粋なcanvas上の座標)
dw 画像の幅を何pxで表示するか(初期値は画像本来の幅)
数値を画像本来のサイズより小さくするとつぶれて、大きくすると伸びる
dh 画像の高さを何pxで表示するか(初期値は画像本来の高さ)
数値を画像本来のサイズより小さくするとつぶれて、大きくすると伸びる

で、setTransform()を使って画像のサイズ調整を行わず
drawImage()で描画するタイミングでサイズ調整すればよくね?
って実際に書くとこうなるわ

ctx.drawImage(img, 0, 0, img_w, img_h, 0, 0, parent_w, img_h * (parent_w / img_w));

これだったらsetTransform()使わなくてもいける

実際、長くなって可読性下がりそうだし分けた方が良いのかもね
とりあえず、勉強になった

メモ:clientWidthは純粋な見えてるコンテンツ領域

clientWidthはpaddingとwidthは含んでborderやmargin、スクロールバーは含まないみたい
borderも含めたかったらoffsetWidthを使ったほうがいいね

clientWidth
clientHeight
widht + padding
border、margin含まない
offsetWidth
offsetHeight
widht + padding + border

他人のコードを読み解くって勉強になるね

Leave a Comment

入力エリアすべてが必須項目です。メールアドレスが公開されることはありません。

内容をご確認の上、送信してください。