background-attachment:fixedが効かないブラウザ用パララックス

iOS (iPhoneやiPad) のブラウザ「Safari」で、背景画像を固定してスクロールしないようにするCSSプロパティのbackground-attachment:fixedが効かない場合があります。

background-attachment:fixedを効果的に使用すると、複数の画像がスクロールによって入れ替わるパララックス (視差) などの面白い演出が可能になります。

今回は、background-attachment:fixedを使用せず、このパララックスを実現する方法を紹介します。

background-attachment:fixedを使わずに画像を固定する方法として良く知られているのが、position:fixedを使う方法です。画像が固定できないなら、画像を設定した要素自体を固定してしまえという訳です。

position:fixedを使用したbackground-attachment:fixedの代替例
body {
  background-image: url(…);
  background-repeat: no-repeat;
  background-attachment: fixed;
  background-position: center;
  background-size: cover;
}
@supports (-webkit-overflow-scrolling: touch) {
  body {
    background: none;
  }
  body:before {
    background-image: url(…);
    background-repeat: no-repeat;
    background-position: center;
    background-size: cover;
    content: "";
    position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
    z-index: -1;
  }
}

上記のコードのように、背景画像を1つbody要素に適用するといった場合には、position:fixedによる代替方法で問題ありません。しかし、複数の画像がスクロールによって入れ替わるパララックスには使用できません。

そこで使用するのがposition:stickyです。

position:stickyのおさらい

position:fixedは祖先要素のレイアウト設定を無視して、ブラウザの描画領域を基準にして固定されます。そのため、どんなにスクロールしても同じ位置のまま動きません。

position:stickyもブラウザの描画領域を基準にして固定されますが、親要素の範囲内でのみ固定されます。

そのため、スクロールによって親要素が描画領域を外れると、親要素と一緒に描画領域から外れます。

position:stickyを設定した要素が固定される条件として、その幅が、親要素のコンテンツ幅 (box-sizing:content-boxの時のwidth値またはheight値) よりも小さく、子要素に占有されていないスペースがある場合に限られます。

position:stickyが設定された要素と親要素と描画領域
┃ = 描画領域の境界
│ = 親要素の境界
■ = position:stickyが設定された子要素
□ = position:stickyが設定されていない子要素
◇ = 子要素に占有されていないスペース

 │□│  │□│  │□│  │◇│  │■│ 
 │□│  │□│  │◇│  │◇│  └─┘ 
┏┿━┿┓┏┿━┿┓┏┿━┿┓┏┿━┿┓┏━━━┓
┃│□│┃┃│■│┃┃│■│┃┃│■│┃┃   ┃
┃│■│┃┃│◇│┃┃│◇│┃┃└─┘┃┃   ┃
┃│◇│┃┃│◇│┃┃└─┘┃┃   ┃┃   ┃
┃│◇│┃┃└─┘┃┃   ┃┃   ┃┃   ┃
┃└─┘┃┃   ┃┃   ┃┃   ┃┃   ┃
┗━━━┛┗━━━┛┗━━━┛┗━━━┛┗━━━┛

position:stickyを使ったパララックス

HTMLコード

<div class="_parallax">
  <p class="_parallax_text">…</p>
  <p class="_parallax_img"><img alt="…" src="…" /></p>
  <p class="_parallax_text">…</p>
  <p class="_parallax_img"><img alt="…" src="…" /></p>
  <p class="_parallax_text">…</p>
  <p class="_parallax_img"><img alt="…" src="…" /></p>
  <p class="_parallax_text">…</p>
</div>

HTMLコードのポイントは、画像部分 (._parallax_img) を挟むようにテキスト部分 (._parallax_text) を配置する点です。

img要素を使用していますが、空の要素にCSSのbackgroundを設定したものでも構いません。

CSSコード

._parallax_text {
  background: #404040; /* ポイント! */
    color: #fff;
  border: solid 32px transparent;
  box-sizing: border-box;
  display: grid;
    place-items: center;
  margin: 0;
    padding: 0 calc(50% - 320px);
  min-height: 100vh; /* ポイント! */
  position: relative;
  z-index: 1; /* ポイント! */
}
._parallax_text:not(:first-child) {
  margin-top: 100vh; /* ポイント! */
}
._parallax_text:not(:last-child) {
  margin-bottom: -100vh; /* ポイント! */
}
._parallax_img {
  background: #fff;
  height: 100vh;
  margin: 0;
  position: -webkit-sticky;
  position: sticky;
    top: 0;
}
._parallax_img img {
  display: block;
  object-fit: cover;
  width: 100%;
    height: 100%;
}

CSSコードのポイントは、テキスト部分 (._parallax_text) に設定したbackground、min-height:100vh、margin-top:100vh、margin-bottom:-100vh、z-index:1の部分です。

position:stickyを設定した画像部分 (._parallax_img) が、テキスト部分に設定したmargin-bottom:-100vhとz-index:1によって、テキスト部分の背後に配置されます。min-height:100vhとbackgorundが設定してあるので、完全に隠れます。

スクロールされると、position:stickyによって固定された画像部分が、margin-top:100vhによって設けられたスペースに表示されます。

CodePenによる実際の表示

See the Pen CSS parallax for iOS by nov (@numerofive) on CodePen.

最近、Internet ExplorerをWindowから投げ捨ててやりました。今まで、どれだけ無駄なコードを書いていたのかを思い知りました。よって、今回のパララックスはIE非対応です。

後は、今回のbackground-attachment:fixedのように、いつまでも残る謎仕様やバグが各ブラウザからなくなってくれると有難いですね。

関連記事