@14islands/r3f-scroll-rig

Progressively enhance any React website with WebGL using react-three-fiber

Usage no npm install needed!

<script type="module">
  import 14islandsR3fScrollRig from 'https://cdn.skypack.dev/@14islands/r3f-scroll-rig';
</script>

README

@14islands/r3f-scroll-rig

Progressively enhance any React website with WebGL using react-three-fiber and a virtual scrollbar.

[awesome example gifs go here]

Background: Progressive Enhancement with WebGL and React

πŸ’‘ This lib has been tested with create-react-app and gatsby.js

Setting up

  1. Add <GlobalCanvas> to your layout. Keep it outside of your router to keep it from unmounting when navigating between pages.
// gatsby-browser.js
import { GlobalCanvas } from '@14islands/r3f-scroll-rig'

export const wrapRootElement = ({ element }) => (
  <>
    {element}
    <GlobalCanvas />
  </>
)
  1. Wrap your page in VirtualScrollbar
// pages/index.js`
import { VirtualScrollbar } from '@14islands/r3f-scroll-rig'

export const HomePage = () => (
    <VirtualScrollbar>
      {(bind) => (
        <article {...bind}>
          <header>
            <h1>Smooth</h1>
          </header>
          <section></section>
          <footer></footer>
        </article>
      )}
    </VirtualScrollbar>
}

πŸ’‘Note: You can use either GlobalCanvas or VirtualScrollbar independently.

Getting Started

show example of a component that tracks the DOM and use the canvas

TBC:

  • Style website using HTML & CSS
  • Use a scrollscene to track a DOM element
  • Render a 3D cube in it's place

Pitfalls & Recommendations

  • All items on the page need a predictable height on load. Always define an aspect ratio using CSS for images and other interactive elements that might impact the document height as they load.
  • If you can’t define height with CSS - you need to trigger reflow() from useScrollRig or useScrollbar after height is final
  • All direct children of VirtualScrollbar will be scrolled separately - keep them small for best performance (~100-200vh)
  • Use CSS animations whenever possible instead of JS for maximum smoothness
  • Intersection Observer with a custom rootMargin is not working well with VirtualScrollbar
  • Consider disabling VirtualScrollbar and all scrolling WebGL elements on mobile - it is usually laggy.
  • Read, make sure you understand and follow all performance pitfalls associated with React and three https://github.com/pmndrs/react-three-fiber/blob/master/markdown/pitfalls.md

API

Hooks

useScrollRig

useCanvas

useScrollbar

Components

<VirtualScrollbar>

The virtual scrollbar moves the DOM content using Javascript and linear interpolation. This allows us to match the speed of objects moving on the fixed GlobalCanvas.

import { VirtualScrollbar } from '@14islands/r3f-scroll-rig'

export const HomePage = () => (
    <VirtualScrollbar>
      {
        // Expects a single functional child
        (bind) => (
          // Return a single DOM wrapper and spread all props {...bind}
          <article {...bind}>  
            <header>
              a scroll section
            </header>
            <section>
              another scroll section...
            </section>
            <footer>
              we all translate in separate layers
            </footer>
          </article>
        )
      }
    </VirtualScrollbar>
}

Each child elements inside the DOM wrapper will be translated individuallly. When an element leaves the viewport it's is no longer animated.

πŸ’‘Note: Make sure you break your page into sections that can be traslated indivually for better scroll performance.

Props

<VirtualScrollbar
  children                // a function child which recieves props and should return a single DOM child with the scroll content
  disabled = false        // Disable the virtual scroll and uses native scroll
  resizeOnHeight = true   // Reflow all components when size changes
  onUpdate                // Callback on each scroll frame `({ current, target, velocity, direction }) => {}`
  lerp = 0.1              // Easing (lerp) for the scroll. (should sync with GlobalCanvas)
  restDelta = 1           // Delta when scroll animation stops
/>

Use without GlobalCanvas

You can import and use VirtualScrollbar in isolation from a separate npm target. This excludes all react-three-fiber, three related imports and allows you to slim down the bundle size.

import { VirtualScrollbar } from '@14islands/r3f-scroll-rig/scrollbar'

πŸ’‘Note: Please keep in mind that this virtual scrollbar impacts accessibility. And even though we keep the borwser's native scrollbar it has not been fully tested with maximum accessibility in mind. Tabbing through form inputs etc might not work as expected.

<GlobalCanvas>

<ScrollScene>