// A devlog by mood.

公開:

WebGLとDOMのスクロール同期、スマホだけズレる問題

fixedキャンバスで起きる追従遅れを、スクロールジャックなしで減らすLusion方式。

DOMでレイアウトした画像を、WebGLの1枚のcanvasで追従描画する構成ってありますよね。
このときスマホだけ「位置がズレる」「追従が遅れて見える」問題にハマりました。

参考にしたのはLusionのこちら。

GitHub - lusionltd/WebGL-Scroll-Sync
Contribute to lusionltd/WebGL-Scroll-Sync development by creating an account on GitHub.
GitHub - lusionltd/WebGL-Scroll-Sync favicon github.com
GitHub - lusionltd/WebGL-Scroll-Sync

ネイティブスクロールは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で端をフェードさせます。

「スクロールジャックなしでどこまで気持ちよくできるか」を考えると、かなり実用的な落としどころでした。

以上、おわり。