タイトルを一見すると「なんだ、よくある記事じゃん」と思われるかもしれませんが、違います。よくある内容ではないのです…。
結局、自力で解決することになりました…。
1. 目的
上の画像のように、ある要素のアスペクト比を維持しつつ、ブラウザの画面サイズ (ビューポートのサイズ) に合わせてギリギリの大きさに表示したいとします。
「幅に合わせて高さを変える」記事ならあるのですが、幅と高さの両方を考慮するものは見当たりませんでした…。
2. やり方
2.1. 方法1: object-fit: contain;
を使う
※この方法は、特定の要素かつ IE と Edge を除いたブラウザでのみ使用することができます
これは <img>
や <video>
, <canvas>
などの置換要素と呼ばれる要素でのみ使用することができます。
参考「object-fit - CSS: カスケーディングスタイルシート | MDN」
以下は <canvas>
での例です。
<style>
html, body, div {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
# content {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
</style>
<canvas id="content" width="1600" height="1200">
</canvas>
<!-- 可視化 -->
<script>
const canvas = document.getElementById('content');
const context = canvas.getContext('2d');
context.fillStyle = '#000';
context.fillRect(0, 0, 1600, 1200);
context.fillStyle = '#cfc';
context.fillRect(20, 20, 1600 - 20 * 2, 1200 - 20 * 2);
</script>
Chrome や Firefox など、大体のブラウザでは思い通りに動作しますが、IE と Edge は上手くいきません。
(IE はともかく Edge で動かないことはちょっと気になります…。)
JS を使用するなら以下のような Polyfill がありますが、今回は JS に頼らない方法を探してみました。
2.2. 方法2: vw
, vh
と max-width
, max-height
を合わせて使う
width
に vh
、height
に vw
を指定すれば行けるのでは…? と思ったら本当に行けました…!
ただしそのままでは幅と高さの大きいほうが画面外にハミだしてしまうので、max-width
, max-height
で制限する必要があります。
以下は <div>
での例です。他の要素でも同様にできると思います。
<style>
html, body, div {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
# wrapper {
width: 100%;
height: 100%;
/* 上下左右中央寄せ */
display: flex;
justify-content: center;
align-items: center;
}
# content {
max-width: 100vw;
max-height: 100vh;
width: calc(100vh * 4 / 3);
height: calc(100vw * 3 / 4);
/* 可視化 */
background-color: #ffc;
border: 10px solid #000;
box-sizing: border-box;
}
</style>
<div id="wrapper">
<div id="content">
</div>
</div>
object-fit: contain;
と違って自動で中央寄せにはなってくれないので、ここではフレックスボックスにして簡単に中央寄せしています (※ IE11, Edge でも動作します) 。
アスペクト比に対応した大きさになるように、ここでは calc()
関数を用いました。vh
と vw
を逆に指定しているのもポイントです。
もしアスペクト比が 1:1 固定なら vmin
が使えるのですが、1:1 でないと単純な 100vw
と 100vh
の比較だけでは上手くいかないので、実用的ではないかなと思います…。
(蛇足になりますが、上記の二つの例では可視化するときのボーダーの太さがそろっていません。単にサンプルコードの手を抜いているだけなので、リサイズが上手くいかずにハミだしたりしているわけではないです…。)
3. まとめ
欲を言えば「ビューポートに合わせる」だけではなく、「任意の親要素に合わせる」ところまでやりたかったのですが、難しそうだったのであきらめました…。
IE は段階的にサポートを終了していますし、Edge はいずれ object-fit
に対応すると思うので、将来的には「方法1」の方がスマートでしょうが、それまではとりあえず「方法2」を使うといいと思います。