APOC

jQueryでbackground: coverみたいなことをする方法

名前がわからない(あるんですかね?)ので「background: coverみたいなやつ」という紹介になってしまうんですが、要は「特定要素の中で隙間を出さずに画像の縦横比を維持したまま拡大・縮小してうまいこと表示する」やつのことです。

長いですね。要は「background: coverみたいなやつ」です。あれ?



昔からあるjQueryプラグインとしては「Backstretch」が有名です。他には「imagefill」や「imagefit」なんかがあるようです。
こういったものを使えば簡単にできるわけですが、ときにはプラグインを使わずに実現したいときがありますよね!

例えば自作のプラグインの機能として組み込む場合とか、プラグインが作るスタイルが気に食わず使いたくないときとか。あると思います(ありました)。

今回はそういたっときに自分で作れるよう作り方を解説していきたいと思います。

先にひとことで作り方を言っておくと、画像とウィンドウの縦横比を比較し一片のサイズを100%にして配置するだけです。

※IE8〜11、Safari(Mac)、Firefox、Chromeで動作確認しています。

やり方を考えてみる

まずはブラウザウィンドウに対して画像を隙間なく埋めて配置する方法を考えていきます。
これができればウィンドウに限らず特定のdiv要素に対して隙間なく埋めるということもできるようになります。

ということで、どうすれば隙間なく画像で埋められるのか考えていきましょう。
とりあえず画像をウィンドウサイズに合わせなければいけないので、パッと思い浮かぶのはこんな感じのやり方でしょうか。

  1. 画像の幅・高さを100%にする
  2. 画像の幅を100%にする
  3. 画像の高さを100%にする

しかし、よくよく考えるとどの方法も問題があります。

まず1番ですが、これは隙間なく配置できるものの画像の縦横比が変わってしまうため画像の見え方が変わってしまいます。

プレビュー(ウィンドウサイズをぐりぐり変えてみてください)

これでは隙間なく配置できたとしても本来見せたいものを正しく見せることができないのでダメですね。

この方法は諦めましょう。

次に2番と3番ですが、これは画像の縦横比とウィンドウの縦横比が異なる場合に隙間ができてしまいます。
例えば、画像サイズが1920×1280でウィンドウサイズが1280×900のとき、画像の縦横比を維持したまま幅を100%にすると1280×853になります。
これでは画像の高さが足りず隙間ができます。

プレビュー(ウィンドウの幅を変えてみてください)

なぜこのように画像の高さが足りなくなるかというと、画像とウィンドウの縦横比が異なりウィンドウの方が縦の比率が高いからです。

それなら高さを100%にすればいいじゃないかと思いますが、確かに「画像サイズが1920×1280でウィンドウサイズが1280×900のとき」なら隙間なく画像を配置することができます。

しかし、今度はウィンドウサイズが1400×900になったときに幅が足りずに隙間ができます。

プレビュー(ウィンドウの高さを変えてみてください)

このように画像とウィンドウの縦横比が異なる場合には、画像の幅・高さのどちらかを100%にしても必ず隙間ができてしまいます。
ウィンドウサイズが固定であればいいのですが、不特定多数のユーザーが見るWebサイトではそれは望めないでしょう。

ちょっと整理してみる

ここでちょっと整理してみましょう。上記のことから、

  1. 画像の幅を100%にすると、ウィンドウの縦横比の縦が高いときに画像の高さが足りなくなる
  2. 画像の高さを100%にするとウィンドウの縦横比の横が高いときに画像の幅が足りなくなる

ということがわかりました。

ということは、画像とウィンドウの縦横比を比較して、if文で1番の条件だったときは画像の高さを100%にし、2番の条件だったときは画像の幅を100%にすることで上手くできそうです。

ただし、縦横比というのは4:3や16:9といったものを指しますので、これではif文で比較できません。

どうやってif文で比較するのか

縦横比では比較できませんので、画像サイズとウィンドウサイズの横に対して縦の比率を比較してみましょう。
この比率は「縦/横」で求めることができます。

先ほどの条件である「画像サイズが1920×1280でウィンドウサイズが1280×900」で計算してみると画像が1.5でウィンドウが1.4222….となります。

縦の比率がわかると「幅/比率」で画像の高さを求めることができます。

この式に当てはめ、画像の幅をウィンドウと同じ1280にしたときの画像の高さを求めると「1280/1.5」で853.333…となり画像の高さが足りないことがわかります。

このことから、縦の比率が「画像>ウィンドウ」だったときには画像の高さを100%にしなければいけないとうことが導き出せました。

これでif文にできそうです。「もし画像の縦の比率がウィンドウ以上の場合、画像の高さを100%にし、そうでなければ画像の幅を100%にする」といった感じです。

コードを書いてみる

ようやくコードを書いてみるところまできました。話が長くなってしまいすみません。
もっと簡潔に伝えられる気もしますが、なぜ比率を比較しているのか、なぜ比率を比較することで隙間なく画像を配置できるのかを理解できなければ意味がないのでなが〜く書いてしまいました。

それでは、ウィンドウに対して画像を隙間なく配置するコードを書いてみましょう。



書いてみました。プレビュー

プレビュー

うまく動いているようです。

それでは各コードの解説をして終わりにしたいと思います。


まずはHTMLとCSSを見ていきましょう。

HTMLではimg#coverとそれを囲うdiv#coverParentの二つがあります。
img#coverはウィンドウサイズに合わせて伸縮するわけですが、場合によっては幅か高さのどちらかがウィンドウサイズを超えてはみ出してしまいます。そのはみ出した部分を非表示にするためにdiv#coverParentが必要です。

そのdiv#coverParentはウィンドウサイズと同じ大きさを持っていなければならないため、絶対位置で配置し幅と高さを100%とします。また、他のコンテンツが上に重なることができるようにしなければならないため重なり順を下げます。

また、img#coverも中央に配置するため、絶対位置で配置しておきます。

これでHTMLとCSSの準備が整いました。あとはjQueryを使って画像のサイズと位置を設定していきます。

続いてJavaScriptの重要な部分をピックアップします。

ここで画像の横に対する縦の比率を計算して変数cover_ratioに代入しています。
画像のサイズをattr()で取得していますが、これはwidth()などでサイズを取得したときに画像の読み込みが終わっていないと0が返ってくるブラウザがあるためです。

もし、imgタグの属性にwidthとheightを正しく設定できない事情がある場合には、loadイベントやimagesLoadedなどのプラグインを使って画像の読み込みが終わるのを待ってから処理を開始する必要がありますので気をつけてください。

この部分はresizeイベントごとに内部の処理を実行するものです。
resizeイベントはページが読み込まれただけでは発生しないため、triggerを使って最初に強制実行させています。

resizeイベントが発生したときにはウィンドウの縦の比率を求めて画像の幅と高さのどちらを100%にするかif文で条件分岐していきます。

ここが前段の話しで挙がった「もし画像の縦の比率がウィンドウ以上の場合、画像の高さを100%にし、そうでなければ画像の幅を100%にする」の部分になります。今回一番のキモです。

そして、その画像を中央に配置します。
leftが50%の位置なので画像の半分を左に寄せると、ちょうど中央に配置されることになります。

このとき、画像のサイズを取得せずにウィンドウの高さから画像の幅を計算していますが、これは先ほども言った通り画像の読み込みが終わる前にwidth()で取得すると0が返ってくるブラウザがあるためです。

今回は画像の比率がわかっていることと、高さはウィンドウと同じという状態なので次の計算式で求めることができます。

高さ * 比率 = 幅
幅  / 比率 = 高さ

以上です。

おわりに

いまは画像や動画を大きく使って見せるデザインが増えていますので、こういった処理をする機会が多いです。
プラグインを使うのは簡単ですが、自分で作れるといざというときに使えますので、そのときにはこの記事を思い返してもらえれば役立つんじゃないかと思います。

(このやり方は自己流のため、もしかしたらもっと効率的で適切なやり方があるかもしれません。もっといい方法があったら教えてください)

ではでは ( ͡° ͜ʖ ͡° )ノシ