パララックス

JavaScriptでパララックスな表現をする場合には、要素ごとにスクロールスピードを変えることで、視差効果を生み出していますが、CSSではtransformの3D表現を使って、要素を実際に画面の手前と奥に配置することで、パララックスを表現できます:)

パララックスの作り方

ドロワーメニューの時と同じコンテンツを使って、下図のような感じに、手前から、アイキャッチ画像、ヘッダーナビ、リンクボタン、タイトルの順になるように、要素を手前に移動させてみます。

各要素を手前に移動させる

要素を手前へ移動させるにはtransformプロパティtranslateZ()を使います。
それから親要素には、奥行きを決めるためのperspectiveプロパティを指定します。ここではbody要素に、perspective: 200pxと指定して、200px離れたところから見てる感じにしてみました。
奥行きの基準点(消失点)は、transform-originプロパティの初期値「親要素の中央」になるはずです。

body { perspective: 200px; }
nav { transform: translateZ(50px); }
h2 { transform: translateZ(20px); }
figure { transform: translateZ(60px); }
.btn { transform: translateZ(40px); }

けど、上手くいきませんね…。
実は、perspectiveプロパティの有効範囲は、プロパティを指定した要素の直下の要素だけなんです…X(
試しに、親要素をすべて取っ払って、動かしたい要素が.container直下になるようにしてみました。なんかぐっちゃぐちゃになりましたね…XO

けれど、大きく見えてるって事は、手前に移動したって事ですよね…、perspectiveプロパティが効いている証拠です!
親要素を外した所為でレイアウトが崩れてるだけだと思うので、親要素なしの状態でレイアウトし直してみます。

なんとか整った感じがします。けれどパララックスはしていないです。
どうやら奥行きの基準点サイト全体の中心になっている様子。基準点ごとスクロールしているから、パララックスしないわけです…X(
サイト全体の中心じゃなくて、下図みたく、常に画面の中心が基準になるようにすれば、上手くパララックスするはずです…!

コンテンツの中心じゃなくて、画面の中心を基準にしたい

ということで、ここでは、コンテンツ全体をひとつのdiv要素で囲って、.containerをというclassを付けました。
ドロワーメニューが開いたらスクロールをロックするの時と同じ要領で、このdiv要素中でスクロールするようにすれば、奥行きの中心が、常に画面の中心になるはずです。
もちろん、perspectiveプロパティも、この要素に指定し直します。

.container {
	position: absolute;
	inset: 0;
	overflow: auto;
	perspective: 200px;
}

ちゃんとパララックスするようになりましたね:D
けれど、手前のものほど大きくなっちゃってるので臨場感だけすごいです…。できれば各要素の大きさは元通りにしたいです。

視点から要素までの距離と、大きさの関係は下図みたいになると思います。距離が近づくほど、反比例して大きくなって見えるんですねぇ。

見た目の大きさは距離と反比例している

例えば、400px離れたところにある要素は、実際の大きさの半分に見えてる事になるので、元の大きさに戻すには、2倍に拡大すればよいという事になります。
100pxのところにある要素なら、2倍の大きさに見えてるので、元の大きさに戻すには、2分の1に縮小(0.5倍)すればよいという事になります。

つまり「要素までの距離 / 基準の距離 = 拡大する値」ということになりますね:D
アイキャッチ画像の場合なら、60px手前に移動してるから、要素までの距離は140pxなので、「140px / 200px = 0.7」。現在の大きさから「0.7」倍すれば元の見ための大きさに戻るということです。

てなもんで、transformプロパティscale()を使って、以下のように追記します。

figure img {
	transform: translateZ(60px) scale(.7);
}

他の要素も同じ要領で、元の見た目の大きさになるように調整すれば、できあがり;D

奥にパララックス

下のサンプルは、各要素の重なり方が、上のサンプルとは逆順になるよう配置してみました。
手前に配置した要素は通常よりも早くスクロールしてましたが、奥に配置すれば、通常よりも遅くスクロールする事になるので、また違った印象になりますね;)

HTML構造そのままでパララックス

とはいえ、パララックスしたいだけなのにHTMLの構造を変えなきゃいけないなんて、文書構造もおかしくなるので良くないと思います…:(
HTMLの構造は元のままで、perspectiveプロパティを効かせられないものかな…。

そんな時には、displayプロパティcontentsという値が重宝します:)

display: contantsと指定した要素は、その要素が無かったことになる、不思議な値です。display: noneとは違って、その要素だけが無かったことになって、子要素は残ります。

section要素に指定すると、section要素だけが無かったことになる

取っ払ってしまっていた親要素(header要素とmain要素とsection要素)をすべて元に戻して、戻した要素に対して、display: contentsを指定してみます。

header,
main,
section {
	display: contents;
}

パララックスしていますね:D
けれど、無かったことになった要素にはスタイルが効かないみたいなので、要素を取っ払った状態のレイアウトの仕方に戻します。

文書構造は保ったままパララックスすることができましたー;D