不定形画像をアスペクト比固定でトリミング

不定形画像をアスペクト比固定でトリミング

CMS などで不特定の画像ファイルを扱うとき、CSS 側で一定のサイズにトリミングして表示したい場合があると思います。そして、フルードイメージのように、縦横比を維持してレスポンシブに表示したいですよね。

今回は、わかってしまえば難しくないけれど、知らないとなかなか思いつかない、不定形画像をアスペクト比(縦横比)固定でトリミングする方法をご紹介します。

サンプルコード

<!--  HTML  -->
  <div id="sample">
    <div>
      <img src="//placehold.jp/2560x2560.png?text=large (2560x2560)" alt="">
    </div>
    <div>
      <img src="//placehold.jp/64x64.png?text=small (64x64)" alt="">
    </div>
    <div>
      <img src="//placehold.jp/512x1024.png?text=portrait (512x1024)" alt="">
    </div>
    <div>
      <img src="//placehold.jp/1024x512.png?text=landscape (1024x512)" alt="">
    </div>
  </div>
/* CSS */
#sample div{
  position: relative;
  overflow: hidden;
}
#sample div::before{
  content: '';
  display: block;
  padding-top: 56.25%;  /* 16:9 */
}
#sample div img{
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
  width: 100%;
}

サンプル Demo

サンプル Demo の試し方

Demo は、挙動の理解がしやすいように、次のコードを追加してあります。

/* CSS */

#sample{
  padding: 1rem;
  width: 320px;
  outline: 1px solid gray;
  box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.1);
  overflow: auto;
  resize: both;
}
#sample div{
  background: #000;
}
#sample div:not(:last-of-type){
  margin-bottom: 1rem;
}

シャドーのかかった外側の四角形は、ブラウザウインドウをイメージしています。右下の角をドラッグしてサイズを変更でき、コンテンツがあふれる場合にはスクロールバーが表示されます。内側にある4つの画像は、外側の四角形に追従してサイズを変化させますが、アスペクト比(縦横比)は一定のまま変わりません。

また、元の画像は、大きすぎたり、小さすぎたり、縦長すぎたり、横長すぎたりしていますが、4つの画像は一定の幅で、画像の中心を基準にトリミングされています。ただし、小さすぎる画像は原寸よりも引き伸ばされてボケていますし、横長すぎる画像は高さが足りずに上下が空いています。

なお、IE、Edge、iOS Safari をお使いの方は、執筆時点では reseize プロパティが実装されていないため、残念ですがドラッグではサイズ変更できません。ブラウザのウインドウ幅を動かしたり、デバイスを横向きにして画面を回転させ、表示の変化をご確認ください。

コード解説 : padding でアスペクト比固定とか

/* CSS L2-5 */

#sample div{
  position: relative;
  overflow: hidden;
}

この div は、内包する画像要素をトリミングするためのフレームです。画像のはみ出した部分を隠すため、overflow プロパティを hidden にしています。

position プロパティを relative にしているのは、次で解説しますが、内包する画像要素を絶対配置した際、その基準点とするためです。 

/* CSS L11-19 */

#sample div img{
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
  width: 100%;
}

一つ飛ばして、先に img を見てみます。

width プロパティを 100%に指定することで、画像要素のサイズが不定でも、親要素 div の幅にぴったり合わせます。div からはみ出した上下の部分は隠れ、トリミングされた状態で表示されます。

position プロパティを absolute にして、絶対配置にしています。基準点となる div に対して上下左右中央寄せになるよう、位置を上下左右から0に、margin を auto に指定しています。(この手法については、関連記事「CSSで上下左右中央寄せ」で詳しく解説しています)

ですが、絶対配置にした画像要素は、親要素のサイズに影響を与えません。このままでは、height を指定していない親要素 div はボックスの高さが0となり、画像要素は完全に隠れて見えなくなってしまいます。

/* CSS L6-10 */

#sample div::before{
  content: '';
  display: block;
  padding-top: 56.25%;  /* 16:9 */
}

レスポンシブデザインにおいて、トリミングされた画像の幅が変わるとき、高さもアスペクト比を保って変化させます。ここでは、HDTVと同じアスペクト比(16:9)にしています。

空の擬似要素をdiv の中に挿入しています。幅が親要素である div と同じになるよう、display プロパティを block にしています。そして、ボックスの高さが 幅の9/16になるよう、padding-top プロパティを56.25%(9/16*100)に指定しています。

padding プロパティを相対値(%)で指定するとき、その基準値は親要素の幅になります。上下のパディングも、親要素の高さではなく、幅が基準です。この場合、div 要素の width 値の 9/16 が、擬似要素の padding-top 値になります。擬似要素は空のため内容の高さ(height)が0になり、padding-top 値がボックスの高さになります(ボックスモデル:すべての要素は、内側から content 領域、padding 領域、border 領域、margin 領域からなる矩形を生成します。width と height は content のサイズです)。幅は div の幅と同じですから、 この擬似要素のアスペクト比は常に16:9となります。

アスペクト比が一定の空ボックスで、フレームを内側から支え、その中心に画像が浮かんでいるような感じになります。

もうひとつの方法 : object-fit とか

今回のサンプル Demo であれば、擬似要素を挿入するのではなく、div 自体に padding-top を指定しても問題はありません。ですが、複数カラムにしたいなど、div とその親要素(今回であれば #sample)の幅が異なるときには、意図したアスペクト比にするための値の指定が複雑になります。擬似要素を用いた方がわかりやすく、汎用性が高いと言えるでしょう。

また、画像が装飾を目的としたものであれば、div の background-image に指定して、background-size プロパティを cover にします。この方法は簡単で確実ですが、その画像がHTML文書上のコンテンツとして意味がない要素なのかどうか、適正な判断をしなければいけません。

実のところ、画像のトリミングには object-fit プロパティを使うこともできます。が、現時点では IE が非対応です。現時点での IE の国内シェアは9.22%、デスクトップの中では14.65%。IE を切り捨ててもかまわないなら、object-fit の使用が本筋で、かつシンプルな方法でしょう。