【CSS】画像をwindow幅に合わせた大きさの正方形で表示する方法

おはようございます。
今日はブラウザに画像を表示する際、window幅に合わせた大きさの正方形で表示をする方法をCSSで書いていきたいと思います。
なお、このブログではRuby on Railsウェブアプリケーションを作る過程で私がつまったところや難しいと感じた部分について記事を書いています。そのため、記事中でのコードはRuby on Railsのコードがでてきます。しかし、今回の内容はHTMLとCSSですので、Rubyなんて知らねえよという方でも参考になると思いますので、よろしくお願いします。

実装後の姿

下の動画のように正方形を維持したままwindow幅に合わせて可変してくれる記述をCSSでしていきたいと思います。
https://gyazo.com/baf904a117b36b0222daae1901389175


前提

前提としては、画像をブラウザ上に表示できている状態とします。以下にHTML(HAML)を示します。

%ul.images__box
  - if event.images.any?
    - event.images.each do |image|
      %li.image_box
        = image_tag image.photograph.url 
  - else
    まだ写真が投稿されていません。



記載の流れ

① 写真が入れられる箱のスタイリング (images__box)
② 写真1枚が入れられる箱のスタイリング (image__box)
③ 写真のスタイリング (img)

という順番で書いていきます。箱と言われてもイメージがつかない人のために概略図を書いておきます。

f:id:shun_0211:20200725102008p:plain


① 写真が入れられる箱のスタイリング (images__box)

まずは一番大きい箱のスタイリングからやっていきます。

.images__box{
  display: flex;


Flexboxを使って画像を横並びにします。ここはこれだけでOKです。

② 写真1枚が入れられる箱のスタイリング (image__box)

次に1枚ずつの箱に対してスタイリングをしていきます。

.image__box{
  width: 23%;
  position: relative;
}
.image__box::before{
  padding-top: 100%;
  content: "";
  display: block;
}


ここが一番理解しにくいところです。まずは、image__boxの横幅を指定します。そして、疑似要素を使用して正方形の箱を作ります。
beforeなどの擬似要素は、対象の要素に擬似的に要素を追加して装飾を適用するセレクタのことです。このbeforeに対してpadding-top: 100%を適用します。CSSでは、高さ方向にpaddingを%で指定した場合、基準値をwidth(100%)から取得する性質があります。つまり、image__boxの横幅と同じpaddingを取ってくれます。これにより、正方形の箱を用意することができるわけです。

また、写真の位置を指定するために position: relative を指定しておきます。


※ ハマるポイントとしては、必ず親要素に当たる要素にulタグをつけ、子に当たる要素にliタグをつける必要があります。そうしないと、原因不明の長方形ができるようになってしまうので注意してください。

③ 写真のスタイリング (img)

最後に写真のスタイリングをして完成です。

img{
  object-fit: cover;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}


position: absolute と top: 0; left: 0 により写真の位置を箱の中にしっかり収めます。当然、写真の縦と横の大きさは箱の大きさと同じにするため、height と weight は100%です。

object-fit: cover を指定することにより、画像をボックスサイズに合わせて縦横比を維持しながらリサイズし、トリミングしてはめ込みます。縦と横のうち、小さい方を基準にして大きい方をトリミングするので、画像は必ず正方形になってくれます。

まとめ

以上になりますが、いかがだったでしょうか。最初はなかなかとっつきにくい内容に見えるかもしれませんが、分解して見ていくと難しいことは何もしてないと思います。最後にCSS全体のコードを載せて終わりにします。ここまで読んでくださり、ありがとうございました。分かりにくいやアドバイス等ありましたらコメントくださると幸いです。では!

    .images__box{
      margin-bottom: 20px;
      text-align: center;
      &__view{
        display: flex;
        width: 100%;
        flex-wrap: wrap;
        .image__box{
          width: 23%;
          position: relative;
          margin-right: 2%;
          margin-bottom: 2%;
          img{
            object-fit: cover;
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
          }
        }
        .image__box::before{
          padding-bottom: 100%;
          content: "";
          display: block;
        }
      }
      .details{
        text-align: center;
        border: 1px solid;
        display: inline-block;
        font-size: 13px;
        padding: 4px 10px;
        border-radius: 10px;
        cursor: pointer;
        margin-top: 10px; 
      }
    }