@14islands/r3f-scroll-rig 中文文档教程

发布于 3年前 浏览 35 项目主页 更新于 3年前

@14islands/r3f-scroll-rig

使用 @react-three/fiber 和虚拟/被劫持的滚动条逐步增强带有 WebGL 的 React 网站。

??? 此库已使用 create-react-appgatsby.js 进行了测试。

Introduction

背景:Progressive Enhancement with WebGL and React

核心是全球共享画布 GlobalCanvas 停留在页面加载之间。 React DOM 组件可以选择在这个画布上绘制东西,同时使用名为 useCanvas 的自定义钩子安装它们。

React DOM 组件可以使用 ScrollSceneViewportScrollScene 自动跟踪它们的位置并在该确切位置绘制 Three.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. Add smooth scrolling to the DOM content

为了在滚动页面时完美匹配 WebGL 和 DOM 内容,需要应用某种 Javascript“平滑滚动”。

我们有两种方法:

  • The VirtualScrollbar will move all of the wrapped DOM content with transform: translate to match the easing of the canvas elements. Each child will be translated independantly for better performance. Keep this in mind to not create sections that are too tall.
  • The HijackedScrollbar will animate window.scrollY to match the easing of the canvas elements.

将您的页面包装在 VirtualScrollbarHijackedScrollbar 中:

// pages/index.js`
import { VirtualScrollbar } from '@14islands/r3f-scroll-rig'

export const HomePage = () => (
    <VirtualScrollbar>
      {(bind) => (
        <article {...bind}>
          <header>
            <h1>I'm a smooth operator</h1>
          </header>
          <section></section>
          <footer></footer>
        </article>
      )}
    </VirtualScrollbar>
}

?????注意: 您可以使用 GlobalCanvasVirtualScrollbar 根据项目需要独立。 如果项目不需要 WebGL,您仍然可以使用滚动条来实现平滑滚动。

Getting Started

这是一个跟踪 DOM 并使用画布在其位置渲染 WebLG 网格的组件的基本示例:它是

如何实现 的作品:

  • The page layout is styled using normal HTML & CSS
  • A component will use the useCanvas hook and pass in a <Scrollscene> to track a DOM element
  • Inside the <ScrollScene> we place a spinning 3D cube and scale it according to the DOM elements size.

沙盒演示:

Basic demo

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

useCanvas

在常规 DOM 组件中使用的挂钩,用于将某些内容渲染到 webGl 画布上。

useCanvas(object: Three.Object3D, deps = []): (props: any) => void

object 将在安装此组件时添加到全局画布,并在卸载时从画布中删除。

useCanvas 返回一个函数,可用于更新传入的 object 的属性。

deps 可用于卸载/重新加载当它们改变时挂载 object 但这通常是不需要的。 使用返回的函数来发送新的道具。

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

function MyMesh() {
  return (
    <mesh>
      <boxGeometry args={[1,1,1]}/>
    </mesh>
  )
}

function MyComponent() {
  useCanvas(<MyMesh/>);
  return ...
}

useScrollRig

挂钩以访问当前滚动装备状态和与渲染相关的功能。

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

const {
  isCanvasAvailable, // True if webgl is enabled and GlobalCanvas has been added to the page
  hasVirtualScrollbar, // True if a smooth scrollbar is currently enabled onm the DOM content
  preloadScene, // request scene to do a preload render before next frame, (scene, camera, layer, callback) => void
  requestRender, // request the global render loop to render next frame
  renderScissor, // renders scene with a scissor to the canvas, ({ gl, scene, camera, left, top, width, height, layer, autoClear, clearDepth}) => void
  renderViewport, // renders a scene inside a viewport to the canvas, ({ gl, scene, camera, left, top, width, height, layer, autoClear, clearDepth}) => void
  reflow, // tigger re-calculation of elements position (called automatically on resize), () => void
  reflowCompleted, // prop updating after positions have been recalculated, number
} = useScrollRig

useScrollbar

useScrollRig 类似,但对于在没有 webgl 的网站上访问平滑滚动条很有用。 不导入任何三个依赖项。

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

const {
  hasVirtualScrollbar, // True if a smooth scrollbar is currently enabled onm the DOM content
  reflow, // tigger re-calculation of elements position (called automatically on resize), () => void
  reflowCompleted, // number, trigger after positions have been recalculated
} = useScrollRig

Components

<VirtualScrollbar>

虚拟滚动条使用 Javascript 和线性插值移动 DOM 内容。 这使我们能够匹配在固定 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>
}

DOM 包装器中的每个子元素都将被单独翻译。 当元素离开视口时,它不再是动画的。

????注意:确保将页面分成可以单独翻译的部分,以获得更好的滚动性能。

缺点:

  • Moving all these composite layers consumes a lot of GPU memory and CPU cycles. Known to cause jank when combining with lots of viewport / scroll animations such as the letter-by-letter animation on 14islands.com

优点:

  • The native scrollbar is preserved and is controlled by the user (we let the user scroll a fake body with height matching the real page).

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
  onUpdate                // Callback on each scroll frame `({ current, target, velocity, direction }) => {}`
  lerp                    // Easing (lerp) for the scroll. (syncs with GlobalCanvas by default)
  restDelta               // Delta when scroll animation stops. (syncs with GlobalCanvas by default)
  threshold = 100         // Extra margin outside the viewport limits for when layers are considered "visible" and start animating
/>

Use without GlobalCanvas

您可以导入和使用VirtualScrollbar 与单独的 npm 目标隔离。 这排除了所有 @react-three/fiberthree 相关的导入,并允许您缩减包的大小。

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

????注意: 请记住,此虚拟滚动条会影响可访问性。 即使我们保留了浏览器的本机滚动条,它也没有经过充分测试并考虑到最大的可访问性。 通过表单输入等选项卡可能无法按预期工作。

<HijackedScrollbar>

被劫持的滚动条接管浏览器滚动条并使用 Javascript 和线性插值对其进行动画处理。 这使我们能够匹配在固定 GlobalCanvas 上移动的对象的速度。

优点:

  • Much better scroll performance as we don't have to move composite layers with CSS transforms.
  • Usage on mobile is much more smooth since it doesn't allow the browser to scroll away the URL bar.
  • Less internal logic needed to position/optimize things on screen as we can have one big page and let the browser scroll it.

缺点:

  • Scrollbar is hijacked and might be worse for accessibility - it listens to the mouse wheel event and prevents default.
  • Mobile scroll is simulated using touch events - not same feel as native scroll.
  • Not visually as smooth as the VirtualScrollbar as it only scrolls by even pixels.
import { HijackedScrollbar } from '@14islands/r3f-scroll-rig'

export const HomePage = () => (
    <HijackedScrollbar>
      {
        // Expects a single functional child
        (bind) => (
          // Return a single DOM wrapper and spread all props {...bind}
          <article {...bind}>
            This content will be smooth scrolled
          </article>
        )
      }
    </HijackedScrollbar>
}

Props

<HijackedScrollbar
  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
  onUpdate                // Callback on each scroll frame `({ current, target, velocity, direction }) => {}`
  lerp                    // Easing (lerp) for the scroll. (syncs with GlobalCanvas by default)
  restDelta               // Delta when scroll animation stops. (syncs with GlobalCanvas by default)
  subpixelScrolling = false // Tell ScrollScenes to scroll by even pixels by default
  location // Pass in router location to detect and recalculate height when navigating. However, it's recommend to unmount and mount the entire component instead.
/>

Subpixel scrolling

似乎(在撰写本文时为 2021 年)浏览器只能按全像素动画滚动。 所以默认情况下,当使用 HijackedScrollbar 时,它会告诉 ScrollSceneViewportScrollScene 做同样的事情,即舍入像素位置。

这导致画布上的运动不太流畅,但在视觉上 DOM 内容与 WebGL 对象同步得更好。 将 subpixelScrolling 设置为 true 以在 GlobalCanvas 上滚动得更平滑,但这会在视觉上使 DOM 内容在低速滚动时看起来非常卡顿。

Use without GlobalCanvas

您可以独立于单独的 npm 目标导入和使用 HijackedScrollbar。 这排除了所有 @react-three/fiberthree 相关的导入,并允许您缩减包的大小。

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

????注意: 请记住,这个被劫持的滚动条会影响可访问性。 即使我们保留了浏览器的本机滚动条,它也没有经过充分测试并考虑到最大的可访问性。 通过表单输入等选项卡可能无法按预期工作。

<GlobalCanvas>

Props

<GlobalCanvas
  children               // r3f child nodes
  gl                     // optional gl overrides
  orthographic = false   // use orthographic camera, perspective by default
  noEvents = true        // no r3f events by default
  colorManagement = true // use sRGB color space
  config                 // override scroll-rig config
  as = Canvas            // make it possible to render as VRCanvas
  // + all other props from r3f Canvas
/>

默认画布 gl 属性:

  antialias = true
  alpha = true
  depth = true
  powerPreference = 'high-performance'
  failIfMajorPerformanceCaveat = true

默认 config 属性:

  debug = false             // true = print render calls to console.log and shader compile errors
  fps = false               // true = stats.js FPS monitor
  autoPixelRatio = true     // use internal PerformanceMonitor to scale pixelRatio
  scrollLerp = 0.1          // scrolling lerp value used by both canvas and scrollbar
  scrollRestDelta = 0.14,   // min delta to trigger animation frame on scroll
  subpixelScrolling = true  // move canvas elements on subpixels

注意:即使 colorManagement开启时,ACESFilmic toneMapping 默认关闭,以便更容易将十六进制颜色与 DOM 匹配。

<ScrollScene>

<ScrollScene
  el                          // DOM element to track (ref)
  children                    // a single functional child
  lerp                        // Easing (lerp) for the scroll. (syncs with GlobalCanvas by default)
  lerpOffset = 0              // Optional offset to move with adjusted lerp compared to global canvas
  renderOrder = 1             // Three.js renderOrder for this Group
  inViewportMargin = size.height/3  // margin for when it's considered out of screen and will stop animating
  visible = true              // Scene visibility
  debug = false               // Render a debug plane and show 50% opacity of DOM element
  setInViewportProp = false   // Set `inViewport` prop on child node when entering viewport (may cause jank)
  positionFixed = false       // Make scene fixed in the viewport instead of moving with scrollbar. Usefull to s
>
  { ({ scale, ...props }) => (
    <mesh>
      <planeGeometry args={[scale.width, scale.height]} />
      <meshBasicMaterial color="turquoise" />
    </mesh>
  )}
</ScrollScene>

子节点将传递以下 props

// inherited props from ScrollScene
el
lerp, lerpOffset
margin
visible
renderOrder

// use this to size your child mesh to the scene!
scale: {
  width, height
}
// transient info about where this element is in the viewport - useful in useFrame()
scrollState: {
  inViewport // boolean - true if currently in viewport
  progress // number - 0 when below viewport. 1 when above viewport
  visibility // number - percent of item height in view
  viewport // number - percent of window height scrolled since visible
}

scene // three js scene that wraps this child node
inViewport // boolean set to true while in viewport if setInViewportProp=true on the ScrollScene
// useFrame render priority (in case children need to run after)
priority // the r3f useFrame priority that these props were calculate for

Under the hood

Render loop

该库在 r3f 中运行全局场景的手动渲染循环。 如果组件请求全局渲染帧,它会以 1000 优先级运行。

要在每一帧上从 useScrollRig 请求全局渲染调用 requestRender()

该库还支持通过调用 renderViewport() 在视口中将单独的场景渲染为单独的渲染通道。 这样我们就可以使用与全局场景不同的灯光或不同的相机来渲染场景。 这就是 ViewportScrollScene 的工作原理。

滚动装备确保全局渲染和视口渲染仅在需要时发生。

您仍然可以使用具有给定优先级的 useFrame 安排您自己的渲染过程。

@14islands/r3f-scroll-rig

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

???? This lib has been tested with create-react-app and gatsby.js.

Introduction

Background: Progressive Enhancement with WebGL and React

At the core there is a global shared canvas GlobalCanvas that stays in between page loads. React DOM components can choose to draw things on this canvas while they are mounted using a custom hook called useCanvas.

React DOM components can use ScrollScene or ViewportScrollScene to automatically track their position and draw a Three.js scene in that exact location. Everything is synched to the scrollbar position.

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. Add smooth scrolling to the DOM content

In order to perfectly match the WebGL and DOM content while scrolling the page, some sort of Javascript "smooth scrolling" needs to be applied.

We have two approaches:

  • The VirtualScrollbar will move all of the wrapped DOM content with transform: translate to match the easing of the canvas elements. Each child will be translated independantly for better performance. Keep this in mind to not create sections that are too tall.
  • The HijackedScrollbar will animate window.scrollY to match the easing of the canvas elements.

Wrap your page in VirtualScrollbar or HijackedScrollbar:

// pages/index.js`
import { VirtualScrollbar } from '@14islands/r3f-scroll-rig'

export const HomePage = () => (
    <VirtualScrollbar>
      {(bind) => (
        <article {...bind}>
          <header>
            <h1>I'm a smooth operator</h1>
          </header>
          <section></section>
          <footer></footer>
        </article>
      )}
    </VirtualScrollbar>
}

????Note: You can use either GlobalCanvas or VirtualScrollbar independently based on the project needs. If the project doesn't need WebGL you can still use the scrollbars to implement smooth scrolling.

Getting Started

This is a basic example of a component that tracks the DOM and use the canvas to render a WebLG mesh in its place:

How it works:

  • The page layout is styled using normal HTML & CSS
  • A component will use the useCanvas hook and pass in a <Scrollscene> to track a DOM element
  • Inside the <ScrollScene> we place a spinning 3D cube and scale it according to the DOM elements size.

Sandbox Demo:

Basic demo

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

useCanvas

Hook used in regular DOM components to render something onto the webGl canvas.

useCanvas(object: Three.Object3D, deps = []): (props: any) => void

object will be added to the global canvas when this component is mounted and removed from the canvas when it unmounts.

useCanvas returns a function that can be used to update the props of the object that was passed in.

deps can be used to unmount/re-mount the object when they change but this is usually not needed. Use the returned function to send new props instead.

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

function MyMesh() {
  return (
    <mesh>
      <boxGeometry args={[1,1,1]}/>
    </mesh>
  )
}

function MyComponent() {
  useCanvas(<MyMesh/>);
  return ...
}

useScrollRig

Hook to access current scroll rig state and functions related to rendering.

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

const {
  isCanvasAvailable, // True if webgl is enabled and GlobalCanvas has been added to the page
  hasVirtualScrollbar, // True if a smooth scrollbar is currently enabled onm the DOM content
  preloadScene, // request scene to do a preload render before next frame, (scene, camera, layer, callback) => void
  requestRender, // request the global render loop to render next frame
  renderScissor, // renders scene with a scissor to the canvas, ({ gl, scene, camera, left, top, width, height, layer, autoClear, clearDepth}) => void
  renderViewport, // renders a scene inside a viewport to the canvas, ({ gl, scene, camera, left, top, width, height, layer, autoClear, clearDepth}) => void
  reflow, // tigger re-calculation of elements position (called automatically on resize), () => void
  reflowCompleted, // prop updating after positions have been recalculated, number
} = useScrollRig

useScrollbar

Similar to useScrollRig but useful to access the smooth scrollbar on websites without webgl. Does not import any three dependencies.

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

const {
  hasVirtualScrollbar, // True if a smooth scrollbar is currently enabled onm the DOM content
  reflow, // tigger re-calculation of elements position (called automatically on resize), () => void
  reflowCompleted, // number, trigger after positions have been recalculated
} = useScrollRig

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 is no longer animated.

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

Cons:

  • Moving all these composite layers consumes a lot of GPU memory and CPU cycles. Known to cause jank when combining with lots of viewport / scroll animations such as the letter-by-letter animation on 14islands.com

Pros:

  • The native scrollbar is preserved and is controlled by the user (we let the user scroll a fake body with height matching the real page).

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
  onUpdate                // Callback on each scroll frame `({ current, target, velocity, direction }) => {}`
  lerp                    // Easing (lerp) for the scroll. (syncs with GlobalCanvas by default)
  restDelta               // Delta when scroll animation stops. (syncs with GlobalCanvas by default)
  threshold = 100         // Extra margin outside the viewport limits for when layers are considered "visible" and start animating
/>

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.

<HijackedScrollbar>

The hijacked scrollbar takes over the browser scrollbar and animates it with Javascript and linear interpolation. This allows us to match the speed of objects moving on the fixed GlobalCanvas.

Pros:

  • Much better scroll performance as we don't have to move composite layers with CSS transforms.
  • Usage on mobile is much more smooth since it doesn't allow the browser to scroll away the URL bar.
  • Less internal logic needed to position/optimize things on screen as we can have one big page and let the browser scroll it.

Cons:

  • Scrollbar is hijacked and might be worse for accessibility - it listens to the mouse wheel event and prevents default.
  • Mobile scroll is simulated using touch events - not same feel as native scroll.
  • Not visually as smooth as the VirtualScrollbar as it only scrolls by even pixels.
import { HijackedScrollbar } from '@14islands/r3f-scroll-rig'

export const HomePage = () => (
    <HijackedScrollbar>
      {
        // Expects a single functional child
        (bind) => (
          // Return a single DOM wrapper and spread all props {...bind}
          <article {...bind}>
            This content will be smooth scrolled
          </article>
        )
      }
    </HijackedScrollbar>
}

Props

<HijackedScrollbar
  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
  onUpdate                // Callback on each scroll frame `({ current, target, velocity, direction }) => {}`
  lerp                    // Easing (lerp) for the scroll. (syncs with GlobalCanvas by default)
  restDelta               // Delta when scroll animation stops. (syncs with GlobalCanvas by default)
  subpixelScrolling = false // Tell ScrollScenes to scroll by even pixels by default
  location // Pass in router location to detect and recalculate height when navigating. However, it's recommend to unmount and mount the entire component instead.
/>

Subpixel scrolling

It seems (2021 at the time of writing) browsers can only animate scroll by full pixels. So by default, when the HijackedScrollbar is used, it tells ScrollScene and ViewportScrollScene to do the same, i.e. round the pixel positions.

This results in less smooth motion on the canvas, but visually the DOM content syncs better with the WebGL objects. Set subpixelScrolling to true to scroll more smooth on the GlobalCanvas, but this will visually make it look like the DOM content is very very janky when scrolling at slow speeds.

Use without GlobalCanvas

You can import and use HijackedScrollbar 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 { HijackedScrollbar } from '@14islands/r3f-scroll-rig/scrollbar'

????Note: Please keep in mind that this hijacked 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>

Props

<GlobalCanvas
  children               // r3f child nodes
  gl                     // optional gl overrides
  orthographic = false   // use orthographic camera, perspective by default
  noEvents = true        // no r3f events by default
  colorManagement = true // use sRGB color space
  config                 // override scroll-rig config
  as = Canvas            // make it possible to render as VRCanvas
  // + all other props from r3f Canvas
/>

Default canvas gl props:

  antialias = true
  alpha = true
  depth = true
  powerPreference = 'high-performance'
  failIfMajorPerformanceCaveat = true

Default config props:

  debug = false             // true = print render calls to console.log and shader compile errors
  fps = false               // true = stats.js FPS monitor
  autoPixelRatio = true     // use internal PerformanceMonitor to scale pixelRatio
  scrollLerp = 0.1          // scrolling lerp value used by both canvas and scrollbar
  scrollRestDelta = 0.14,   // min delta to trigger animation frame on scroll
  subpixelScrolling = true  // move canvas elements on subpixels

Note: even though colorManagement is turned on, ACESFilmic toneMapping is turned off by default to make it easier to match hex colors with the DOM.

<ScrollScene>

<ScrollScene
  el                          // DOM element to track (ref)
  children                    // a single functional child
  lerp                        // Easing (lerp) for the scroll. (syncs with GlobalCanvas by default)
  lerpOffset = 0              // Optional offset to move with adjusted lerp compared to global canvas
  renderOrder = 1             // Three.js renderOrder for this Group
  inViewportMargin = size.height/3  // margin for when it's considered out of screen and will stop animating
  visible = true              // Scene visibility
  debug = false               // Render a debug plane and show 50% opacity of DOM element
  setInViewportProp = false   // Set `inViewport` prop on child node when entering viewport (may cause jank)
  positionFixed = false       // Make scene fixed in the viewport instead of moving with scrollbar. Usefull to s
>
  { ({ scale, ...props }) => (
    <mesh>
      <planeGeometry args={[scale.width, scale.height]} />
      <meshBasicMaterial color="turquoise" />
    </mesh>
  )}
</ScrollScene>

The child node will be passed the following props:

// inherited props from ScrollScene
el
lerp, lerpOffset
margin
visible
renderOrder

// use this to size your child mesh to the scene!
scale: {
  width, height
}
// transient info about where this element is in the viewport - useful in useFrame()
scrollState: {
  inViewport // boolean - true if currently in viewport
  progress // number - 0 when below viewport. 1 when above viewport
  visibility // number - percent of item height in view
  viewport // number - percent of window height scrolled since visible
}

scene // three js scene that wraps this child node
inViewport // boolean set to true while in viewport if setInViewportProp=true on the ScrollScene
// useFrame render priority (in case children need to run after)
priority // the r3f useFrame priority that these props were calculate for

Under the hood

Render loop

This library runs a manual render loop of the global scene inside r3f. It runs with priority 1000 if a component has requested a global render frame.

To request global render call requestRender() from useScrollRig on each frame.

This library also supports rendering separate scenes in viewports as a separate render pass by calling renderViewport(). This way we can render scenes with separate lights or different camera than the global scene. This is how ViewportScrollScene works.

The scroll rig makes sure global renders and viewport renders only happens if it's needed.

You can still schedule your own render passes using useFrame with a given priority.

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文