【CSS】object-fitの使い方[IE11対応]

【CSS】object-fitの使い方[IE11対応]

画像のトリミングをCSSでできる「object-fit」プロパティ

今回はCSSの「object-fit」プロパティについてのメモ。

object-fitプロパティは、<img>要素や<video>要素、<iframe>要素などの内容をコンテナー(枠)に合わせてどのように配置するかを指定することができます。

指定することができるのは、5種類。

contain

アスペクト比を保ったまま枠に合わせて画像が収まりきるように拡大縮小して配置されます

cover

アスペクト比を保ったまま枠いっぱいに埋まるように拡大縮小して配置されます

fill

画像を枠いっぱいに広げますが、アスペクト比は無視されて全体が埋まるように拡大縮小するので、被写体の映った写真などの場合は画像が歪むことがあります。初期値はこれになっています。

none

拡大縮小せずにそのままの画像サイズを適用します。

scale-down

none と contain のどちらか小さい方のサイズに合わせて拡大縮小します。

指定の仕方はbackground-sizeプロパティと似ていて、containもしくはcoverを主に使うことが多くなると思います。僕はfillnonescale-downは今までの案件で一度も使ったことがないです。noneについてはレスポンシブでスマホとPCで見え方を変えたいっていう時は使えるかもしれません。

//この2つはbackground-sizeとほぼ同じ挙動
object-fit: contain;
object-fit: cover;

//他の3つはあまり利用シーンが思いつかない
object-fit: fill;
object-fit: none;
object-fit: scale-down;

わかりやすいように実際に使用したデモも用意しました。

object-fitのデモ

トリミングの位置調整ができる「object-position」

トリミングの位置についてはobject-positionというプロパティを使います。こちらもbackground-positionと似ていて同じ指定方法が可能なので使い方に迷うことはあまりないと思います。

//background-positionと同じ指定方法
object-position: 50% 50%;
object-position: left top;
object-position: 100px 150px;

object-fit対応ブラウザ

IE11以外はすべての最新ブラウザで対応しています。

Can I use 「object-fit」のブラウザ対応状況

IE11でも使えるようにするにはpolyfillが必要になります。
検索するといくつか出てきますが、個人的におすすめなのが「object-fit-images」というpolyfillです。

fregante / object-fit-images

https://github.com/fregante/object-fit-images

簡単に使い方を紹介しておきます。

「object-fit-image」polyfillでIE11対応

まずはパッケージをインストール。

$ yarn add -D object-fit-image

//または

$ npm i -D object-fit-images

次にobject-fitを適用したい<img>タグにclassを指定します。class名は任意のもので大丈夫です。

<img class="ofi" src="ofi-image.jpg">

CSSを書きます。

.ofi{
  object-fit: cover;
  font-family: 'object-fit: cover;';
}

ここがポイントになりますが、font-familyにobject-fitの記述をクォーテーションで囲んでそのまま書き込みます。<img>タグにfont-familyを指定するのはちょっと気持ち悪いかもしれませんが、このpolyfill以外でそんな記述をすることはまずないですし、何かと競合することも考えにくいので、そういう書き方だと思って割り切ってしまったら慣れます。笑

また、object-positionも同時に使う場合は下記のようにします。

.ofi{
  object-fit: cover;
  object-position: top right;
  font-family: 'object-fit: cover; object-position: top right;';
}

単純にfont-familyにそのままobject-positionの記述を追記する形でOKです。

最後にJS。importrequireで呼び出します。

import objectFitImages from 'object-fit-images';

//または

const objectFitImages = require('object-fit-images');

CDNを使う場合はこちらをhtmlファイルに読み込ませます。

<script src="https://cdnjs.cloudflare.com/ajax/libs/object-fit-images/3.2.4/ofi.min.js"></script>

そして関数を実行。

objectFitImages();

基本的にはこれでfont-familyが指定されたすべての<img>タグにpolyfillが効きます。

もちろんタグによって個別に指定することも可能です。

//.ofiのみのセレクタ指定でもOK
objectFitImages('.ofi');

//NodeListもOK
const ofis = document.querySelectorAll('.ofi');
objectFitImages(ofis);

//要素単体もOK
const ofi = document.getElementById('ofi');
objectFitImages(ofi);

//jQueryオブジェクトもOK
const $ofi = $('.ofi');
objectFitImages($ofi);

また、レスポンシブ対応で、画面のリサイズ時に自動で再適用してくれるオプションもあります。

objectFitImages('.ofi', {watchMQ: true});

//セレクタをnullにした場合は自動判定
objectFitImages(null, {watchMQ: true});

「object-fit-image」の使い方はこれだけ。簡単ですね。

他にも設定以外の標準機能として、srcset属性のサポートにも対応しています。レスポンシブイメージで<picture>要素を使う場合に「picturefill」というpolyfillを使うことがあるのですが、それをサポートする仕様になっているので同時に使えます。設定いらずで読み込ませるだけで対応しているので便利!

これでIE11でもバッチリ対応できますね!

「object-fit-image」polyfillの注意点

polyfillも完璧ではありませんので、中にはうまく表示ができないケースがあります。僕が実際に使っていて遭遇したケースと解決策を一部メモしておきます。

コンテナー(枠)にflexboxを使うと表示がずれる

例えば高さに制限があるコンテナの中心に画像を置きたい場合に以下のような記述を書くとします。入る画像は固定ではなくユーザーが設定するパターンでの想定です。

<div class="flex-center">
  <img class="ofi" src="ofi-image.jpg">
</div>
.flex-center{
  min-height: 300px; //下限を300px
  display: flex;
  align-items: center;
  justify-content: center;
}

.ofi{
  max-width: 100%;
  height: auto;
  max-height: 600px; //高さの上限を600px
  object-fit: cover;
  font-family: 'object-fit: cover;';
}

この場合はflexboxのバグと競合して画像の位置がずれます。
最近はflexboxが便利なのでセンタリングなどに使いがちだったのですが、やはりIE対応が必須の場合は思わぬバグに遭遇するので、positionを使ったセンタリングや、divを増やしてラッパーにflexboxを使ってみるなど他の方法で柔軟に対応しましょう。

Ajaxなどで後から追加される要素には自動で適用されない

Ajaxで記事の追加読み込みなどを行なった場合に、追加する要素に画像が含まれている場合は、再度「object-fit-images」を適用する必要があります。

$.ajax({
  url        : url,
  method     : 'get',
})
.done((data, textStatus, jqXHR) => {
  
  // ~~ 省略

  //記事のDOMを追加する処理の後にobject-fit-imagesを適用
  $container.append(newList);
  objectFitImages();

});

これでOKです。
ただこのままだと全部の<img>タグに再適用するので、追加する要素が絞れる場合は、objectFitImages()の引数に入れてあげれば追加する要素のみに適用することができます。

おわりに

object-fit、ここ2、3年で個人的には必須といっていいぐらい使ってます!
WordPress案件が多いのですが、メイン画像・ブログ記事一覧・商品リスト一覧、バナーなど、ユーザーが任意で入れる画像がバラバラの大きさやアスペクト比になってしまうことが想定できる場合、画像の大きさをきっちり指定しなくてもある程度表示を制御できるのでとても重宝しています。IE11対応も簡単なpolyfillで済むのでいい感じ。object-fitを知るまでは、<img>タグでこういう処理ができないから仕方なくbackgroundに置き換えて・・・みたいなことをしていたのですがもうそんなイビツなコーディングをしなくて済みます。

まだ使っていない方はぜひ使ってみてください!