今回はCSSの「object-fit」プロパティについてのメモ。
object-fit
プロパティは、<img>
要素や<video>
要素、<iframe>
要素などの内容をコンテナー(枠)に合わせてどのように配置するかを指定することができます。
指定することができるのは、5種類。
アスペクト比を保ったまま枠に合わせて画像が収まりきるように拡大縮小して配置されます
アスペクト比を保ったまま枠いっぱいに埋まるように拡大縮小して配置されます
画像を枠いっぱいに広げますが、アスペクト比は無視されて全体が埋まるように拡大縮小するので、被写体の映った写真などの場合は画像が歪むことがあります。初期値はこれになっています。
拡大縮小せずにそのままの画像サイズを適用します。
none
と contain
のどちらか小さい方のサイズに合わせて拡大縮小します。
指定の仕方はbackground-size
プロパティと似ていて、contain
もしくはcover
を主に使うことが多くなると思います。僕はfill
、none
、scale-down
は今までの案件で一度も使ったことがないです。none
についてはレスポンシブでスマホとPCで見え方を変えたいっていう時は使えるかもしれません。
//この2つはbackground-sizeとほぼ同じ挙動
object-fit: contain;
object-fit: cover;
//他の3つはあまり利用シーンが思いつかない
object-fit: fill;
object-fit: none;
object-fit: scale-down;
わかりやすいように実際に使用したデモも用意しました。
トリミングの位置についてはobject-position
というプロパティを使います。こちらもbackground-position
と似ていて同じ指定方法が可能なので使い方に迷うことはあまりないと思います。
//background-positionと同じ指定方法
object-position: 50% 50%;
object-position: left top;
object-position: 100px 150px;
IE11以外はすべての最新ブラウザで対応しています。
IE11でも使えるようにするにはpolyfillが必要になります。
検索するといくつか出てきますが、個人的におすすめなのが「object-fit-images」というpolyfillです。
fregante / object-fit-images
https://github.com/fregante/object-fit-images
簡単に使い方を紹介しておきます。
まずはパッケージをインストール。
$ 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。import
かrequire
で呼び出します。
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でもバッチリ対応できますね!
polyfillも完璧ではありませんので、中にはうまく表示ができないケースがあります。僕が実際に使っていて遭遇したケースと解決策を一部メモしておきます。
例えば高さに制限があるコンテナの中心に画像を置きたい場合に以下のような記述を書くとします。入る画像は固定ではなくユーザーが設定するパターンでの想定です。
<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で記事の追加読み込みなどを行なった場合に、追加する要素に画像が含まれている場合は、再度「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
に置き換えて・・・みたいなことをしていたのですがもうそんなイビツなコーディングをしなくて済みます。
まだ使っていない方はぜひ使ってみてください!