パララックス

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
とは違って、その要素だけが無かったことになって、子要素は残ります。

取っ払ってしまっていた親要素(header要素とmain要素とsection要素)をすべて元に戻して、戻した要素に対して、display: contents
を指定してみます。
header,
main,
section {
display: contents;
}
パララックスしていますね:D!
けれど、無かったことになった要素にはスタイルが効かないみたいなので、要素を取っ払った状態のレイアウトの仕方に戻します。
文書構造は保ったままパララックスすることができましたー;D。