LoginSignup
7
5

More than 5 years have passed since last update.

CSS だけでアスペクト比を維持しながら幅と高さをビューポートに合わせる (IE11, Edge 対応)

Posted at

タイトルを一見すると「なんだ、よくある記事じゃん」と思われるかもしれませんが、違います。よくある内容ではないのです…。
結局、自力で解決することになりました…。

1. 目的

image.png

上の画像のように、ある要素のアスペクト比を維持しつつ、ブラウザの画面サイズ (ビューポートのサイズ) に合わせてギリギリの大きさに表示したいとします。

「幅に合わせて高さを変える」記事ならあるのですが、幅と高さの両方を考慮するものは見当たりませんでした…。

2. やり方

2.1. 方法1: object-fit: contain; を使う

※この方法は、特定の要素かつ IE と Edge を除いたブラウザでのみ使用することができます

これは <img><video>, <canvas> などの置換要素と呼ばれる要素でのみ使用することができます。

参考「object-fit - CSS: カスケーディングスタイルシート | MDN

以下は <canvas> での例です。

方法1: object-fit: contain; を使う
<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 に頼らない方法を探してみました。

参考「GitHub - bfred-it/object-fit-images: 🗻 Polyfill object-fit/object-position on : IE9, IE10, IE11, Edge, Safari, ...

2.2. 方法2: vw, vhmax-width, max-height を合わせて使う

widthvhheightvw を指定すれば行けるのでは…? と思ったら本当に行けました…!
ただしそのままでは幅と高さの大きいほうが画面外にハミだしてしまうので、max-width, max-height で制限する必要があります。

以下は <div> での例です。他の要素でも同様にできると思います。

方法2: vw, vh と max-width, max-height を合わせて使う
<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() 関数を用いました。vhvw を逆に指定しているのもポイントです。

もしアスペクト比が 1:1 固定なら vmin が使えるのですが、1:1 でないと単純な 100vw100vh の比較だけでは上手くいかないので、実用的ではないかなと思います…。

(蛇足になりますが、上記の二つの例では可視化するときのボーダーの太さがそろっていません。単にサンプルコードの手を抜いているだけなので、リサイズが上手くいかずにハミだしたりしているわけではないです…。)

3. まとめ

欲を言えば「ビューポートに合わせる」だけではなく、「任意の親要素に合わせる」ところまでやりたかったのですが、難しそうだったのであきらめました…。

IE は段階的にサポートを終了していますし、Edge はいずれ object-fit に対応すると思うので、将来的には「方法1」の方がスマートでしょうが、それまではとりあえず「方法2」を使うといいと思います。

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5