これは CSS Programming Advent Calendar 2012 の 14 日目の記事です。
8 月の末に、WebKit に position: sticky
というプロパティが追加されました。
position: sticky
が指定された要素は、親要素との位置関係によって、position: fixed
または position: relative
が指定されたかのように振る舞います。
例えば、ある程度スクロールするとサイドバーやナビゲーションが固定されるサイトをたまに見かけますが(例えば Togetter, LESS のサイト, etc.)その動作が CSS で指定できるようになったわけです。
HTML5Rocks に詳しい解説があるので、「ただしい」使用法についてはそちらを参照してください。
現在は Chrome (おそらく 23 以上)に実装が載っています。
about:flags
で「試験運用版の WebKit 機能を有効にする」をオンにする必要があります。
さて、sticky positioned な要素の親要素に、CSS Transforms を適用することを考えます。 すると、親要素との位置関係(より正しくは、親要素の bounding box との位置関係)が変化するため、sticky な要素の動作も変形されます。
これは position: fixed
や relative
には見られない特徴的な挙動です。
この挙動を「ただしくなく」利用して、例えばスクロール時にいろいろな方向に要素が動く効果(いわゆる「高級ペライチ」、「パララックス」)を CSS で実現できます。
- Demo 2. Scrolling Effects
- 使っている SCSS mixins sticker.scss
ブラウザが適当に処理してくれるのか、要素が巨大だったり数が多くても、割と軽い動作になっています。 意図したとおりに動かすには計算がやや煩雑ですので、上のように CSS プリプロセッサを使うのがよいでしょう。 また、3D Transforms を組み合わせると、スクロールに応じて大きさを変化させたりということも可能です。 しかしさらに面倒になるので上の mixin にはその機能はありません……。
sticker.scss では sqrt()
や arctan()
を自前で用意していますが、Sass の Custom Functions などで既存の実装を使うほうがよいと思います。
(ただし、SCSS で自前で書いても、コンパイル時間や精度など特に問題にはなりませんでした。)
sticky な要素が一体どこに配置されるのか計算してみたいと思います。 まず、要素の bounding box とは、四辺が水平または垂直な長方形で、その要素を完全に覆うもののうち、最小のものをいうことにします。
以下では、ページは次のような状態になっているとします。
<style>
#parent {
transform: ...; /* 実際には vendor prefix がつきます、以下同様 */
}
#sticky {
position: sticky;
left: ...; /* または right */
top: ...; /* または bottom */
}
</style>
<div id="parent">
<div id="sticky"></div>
</div>
親要素(#parent
)に Transform が設定されたとき、#sticky
要素の位置は次の順序で計算できます。
- 親要素の Transform 適用後の bounding box を算出する
#sticky
を、まずはfixed
だと思って配置する- 1 の bonding box にすっぽり入るまで最短距離で移動する。 元から入っていれば何もしない。
- bounding box 内での座標を調べる
#sticky
を親要素内のその座標にあらためて配置する- Transform を適用する
最後の (6) で Transform がかかるために、例えば上のように #parent
に回転をかければ、スクロールしたときの「移動方向」を変えることができます。
拡大または縮小をかければ、「移動量」を変えることができます。
あとは #sticky
にも逆の変形をかけると、あたかも別の方向に、別の速度でスクロールしているかのように見えるわけです。
(1) で bounding box の left/top (または right/bottom)が、変形前と変わらないように適当に平行移動してやると、(3) と (5) でのずらす処理を考えなくてよく、配置しやすいと思います。 sticker.scss はそうなっているので気になる方は試してみてください。
以上に述べたことは現在の WebKit の実装ではこういうことができるというだけで、将来にわたって、また他のブラウザで(実装されたとしても)同じことができる保証はもちろんありません。 スクロールに反応して変なことができそうなので、ぜひこのまま残ってくれると嬉しいのですが。 めでたし、めでたし……
明日は ksk1015 さんです!