WebGLとDOMのスクロール同期、スマホだけズレる問題
fixedキャンバスで起きる追従遅れを、スクロールジャックなしで減らすLusion方式。
DOMでレイアウトした画像を、WebGLの1枚のcanvasで追従描画する構成ってありますよね。
このときスマホだけ「位置がズレる」「追従が遅れて見える」問題にハマりました。
参考にしたのはLusionのこちら。
ネイティブスクロールはrequestAnimationFrameと完全には同期しない、というシンプルな話です。
スクロールがrAFの合間に進むと、canvas側は次のフレームまで古い位置で描画されます。そのため、DOMは動いているのにWebGLだけ遅れて見えます。
Lusionの発想転換
よくある回避策はLenisなどでスクロール自体をrAF同期に寄せる方法(いわゆるスクロールジャック)ですが、Lusionはスクロール側ではなく、canvasをfixedにしないやり方を取っています。
- canvasを
position: fixedではなくposition: absoluteにする - ページと一緒にcanvas自体も物理的にスクロールさせる
rAF中にtopオフセットをscrollYに合わせて調整する
これで、rAFの合間にスクロールしてもcanvasがDOMと一緒に動くので、
WebGLだけ追従が遅れて見える感じがかなり減ります。
実運用では、いっそスクロールジャックしたり、スマホだけWebGL演出を切ったりすることもよくあります。
ただ自分の感覚では、スマホはネイティブ慣性が強いぶん、スクロールジャックを重ねると「指と画面のズレ感」が出やすいです。
そこで、スマホでもWebGLを活かしつつズレを減らす方法がないか、GitHubやSNSなどで探し回りました。
トレードオフ
ただ、この方式は万能ではなくて、弱点もあるんですよね。
- 高速スクロール時にcanvasの描画範囲外が見えて、クリップが出ることがある
- canvasを
fixedでビューポートに固定する構成から、absoluteで使う構成に変える必要がある
クリップへの対策として、canvasの上下に余白を持たせて多めに描画します。デモでは上下に25%の余白を取っています。 余裕があれば、framebufferで端をフェードさせます。
「スクロールジャックなしでどこまで気持ちよくできるか」を考えると、かなり実用的な落としどころでした。
以上、おわり。