Releases: pmndrs/react-three-fiber
v8.1.0
What's Changed
- fix(core):cannot remove effects correctly when use addEffect/addAfter… by @wangzongqi1001 in #2357
- feat: expose position information on
state.size
by @kmannislands in #2339
New Contributors
- @wangzongqi1001 made their first contribution in #2357
- @kmannislands made their first contribution in #2339
Full Changelog: 8ad0559...v8.1.0
v8.0.x
v8 Migration Guide
Changes and new features with v8 and react 18
Work on version 8 has begun 3 Sep 2021 and is perhaps the biggest update to Fiber yet. We've tried our best to keep breaking-changes to a minimum, they mostly affect rarely used api's like attach
. This release brings a ton of performance related fixes, but also includes some new and ground breaking features.
We would like to express our gratitude to the community for their continued support, as well as to all our contributors. 🎉
React Native support
With React 18 and Expo 43, you can now ship threejs goodness across web and native. These apps are not confined to a web-view, they are truly native OpenGLES.
Now expo-gl, which does the hard lifting, has existed for a while, but we've addressed some of the common concerns related to interop and making the three eco system readily work in the native space. For instance, you handle assets the same exact way you'd treat them in a web app.
See installation to get started with managed Expo or bare React Native apps.
- import { Canvas, useLoader } from '@react-three/fiber'
+ import { Canvas, useLoader } from '@react-three/fiber/native'
- import { useGLTF } from '@react-three/drei'
+ import { useGLTF } from '@react-three/drei/native'
This is complete with support for Pressability events and native threejs loaders. See events for a complete list of events.
<mesh
onClick={(e) => console.log('onPress')}
onPointerDown={(e) => console.log('onPressIn')}
onPointerUp={(e) => console.log('onPressOut')}
onDoubleClick={(e) => console.log('onLongPress')}
onPointerOver={(e) => console.log('onHoverIn')}
onPointerOut={(e) => console.log('onHoverOut')}
onPointerMove={(e) => console.log('onPressMove')}
// Not implemented
// onContextMenu={(e) => console.log('context menu')}
// onWheel={(e) => console.log('wheel spins')}
/>
import React, { Suspense } from 'react'
import { useGLTF } from '@react-three/drei/native'
import { Canvas } from '@react-three/fiber/native'
import modelPath from './assets/model.glb'
function Model(props) {
const { scene } = useGLTF(modelPath)
return <primitive {...props} object={scene} />
}
export default function App() {
return (
<Canvas>
<Suspense fallback={null}>
<Model />
</Suspense>
</Canvas>
)
}
React Native support was submitted by @Cody_J_Bennett. 🎉
Zustand and suspend-react
Fiber uses zustand to manage state. It's a simple, powerful, and performant state management library for React. And suspend-react to manage async ops and suspense (useLoader for instance). You can use these in your projects as well as they are to get closer interop with Fiber.
New pixel ratio default
The default DPR has changed from 1
to [1, 2]
, which will clamp between 1 and 2, but prefer 2, depending on the screen's native pixel ratio.
This was the most common setting in the wild, so it was brought in as a better default.
- <Canvas dpr={[1, 2]} />
+ <Canvas />
Color management
Color management is now being handled by Three R139. Therefore we set THREE.ColorManagement.legacyMode
to false
and cede to touch colors and textures since everything will now be converted from sRGB to linear space by Three itself.
While you can of course use Fiber with any Three version you like we recommend updating to R139.
Check out https://threejs.org/docs/index.html#manual/en/introduction/Color-management for information.
Automatic concurrency
Concurrency is now part of React 18, which automatically switches between blocking (default) and concurrent (async).
- <Canvas mode="concurrent" />
+ <Canvas />
React 18 introduces the startTransition
and useTransition
APIs to defer and schedule expensive operations and state updates. Use these to de-prioritize expensive operations.
import { startTransition } from 'react'
import { Points } from '@react-three/drei'
const [radius, setRadius] = useState(1)
const positions = calculatePositions(radius)
const colors = calculateColors(radius)
const sizes = calculateSizes(radius)
<Points
positions={positions}
colors={colors}
sizes={sizes}
onPointerOut={() => startTransition(() => setRadius(prev => prev + 1))}
>
<meshBasicMaterial vertexColors />
</Points>
Examples
Please be careful, this is an extreme stress test. It creates so much load that without React the browser will freeze or crash.
Conditional rendering with frameloop
frameloop
can now be toggled to render conditionally. This is useful to toggle on user interaction or while in frame.
const [frameloop, setFrameloop] = useState('never')
<Canvas
frameloop={frameloop}
onClick={() => setFrameloop('always')}
/>
Another usecase would be using intersection observers to stop the canvas when it's out of view.
const canvasRef = useRef()
const [frameloop, setFrameloop] = useState('never')
useEffect(() => {
const observer = new IntersectionObserver(([{ isIntersecting }]) => {
setFrameloop(isIntersecting ? 'always' : 'never')
}, {})
observer.observe(canvasRef.current)
return () => observer.disconnect()
}, [])
<Canvas ref={canvasRef} frameloop={frameloop} />
Expanded gl prop
The gl
prop can now accept both constructor args and renderer properties like the camera
prop.
<Canvas gl={{ alpha: false, physicallyCorrectLights: true }} />
It can also accept a synchronous callback to manually create a renderer. This allows you to use any custom renderer you want.
<Canvas gl={(canvas) => new Renderer({ canvas })} />
Improved WebXR handling
Automatic WebXR switching
The vr
prop was removed in favor of automatic WebXR switching. Whenever a session is requested, XR features are enabled, and the renderer will render at the native refresh rate. The inverse is true when exiting a session.
frameloop
will not be respected while in a session.
- <Canvas vr />
+ <Canvas />
Extended useFrame
In addition to the automatic rendering, useFrame will expose the current XRFrame
obtained via XRSession#requestAnimationFrame.
useFrame((state: RootState, delta: number, frame?: THREE.XRFrame) => { ... })
This removes the need for custom rendering loops when using WebXR pose data and abstractions like useXRFrame
of @react-three/xr.
Manual camera manipulation
By default Fiber is responsive and will set up cameras properly on resize (aspect ratio etc).
Cameras can be controlled manually by setting manual
to true in camera
. This will opt out of projection matrix recalculation when the drawing area resizes.
<Canvas camera={{ manual: true }}>
This is also supported by all cameras that you create, be it a THREE.PerspectiveCamera or drei/cameras, put manual
on it and Fiber will not touch it.
import { PerspectiveCamera } from '@react-three/drei'
;<Canvas>
<PerspectiveCamera makeDefault manual />
</Canvas>
Unified attach API
Previously, attach had multiple signatures:
attach="name"
attachObject={["name", "attribute"]}
attachArray="name"
attachFns={["add", "remove"]}
attachFns={[(self, parent) => parent.add(self), (self, parent) => parent.remove(self)]}
This is now a single, unified signature with support for piercing and named attach functions or custom handlers.
// Attach foo to parent.a
<foo attach="a" />
// Attach foo to parent.a.b and a.b.c (nested object attach)
<foo attach="a-b" />
<foo attach="a-b-c" />
// Attach foo to parent.a[0] and [1] (array attach is just object attach)
<foo attach="a-0" />
<foo attach="a-1" />
// Attach foo to parent via explicit add/remove functions
<foo attach={(parent, self) => {
parent.add(self)
return () => parent.remove(self)
} />
// The same as a one liner
<foo attach={(parent, self) => (parent.add(self), () => parent.remove(self))} />
Real-world use-cases:
Attaching to nested objects:
- <directionalLight
- castShadow
- position={[2.5, 8, 5]}
- shadow-mapSize={[1024, 1024]}
- shadow-camera-far={50}
- shadow-camera-left={-10}
- shadow-camera-right={10}
- shadow-camera-top={10}
- shadow-camera-bottom={-10}
- />
+ <directionalLight castShadow position={[2.5, 8, 5]} shadow-mapSize={[1024, 1024]}>
+ <orthographicCamera attach="shadow-camera" args={[-10, 10, 10, -10]} />
+ </directionalLight>
<bufferGeometry>
- <bufferAttribute attachObject={['attributes', 'position']} count={count} array={vertices} itemSize={3} />
+ <bufferAttribute attach="attributes-position" count={count} array={vertices} itemSize={3} />
</bufferGeometry>
Arrays must be explcit now:
<mesh>
- {colors.map((color, index) => <meshBasicMaterial key={index} attachArray="material" color={color} />}
+ {colors.map((color, index) => <meshBasicMaterial key={index} attach={`material-${index}`} color={color} />}
</mesh>
Spread Canvas props
The <Canvas />
can now accept non-render props to spread as native props: styles, classes, events, a11y, ...
v7.X.X
There are no breaking changes for v7.0.0
v6.2.3
RELEASING: Releasing 2 package(s) Releases: @react-three/fiber@6.2.3 @react-three/test-renderer@6.2.3 [skip ci]
v6.0.13
6.x
Monorepo
--- npm install react-three-fiber
+++ npm install @react-three/fiber
Features
- Use r3f without react-dom (saves ~30kb)
import React from 'react'
import { render } from 'react-three-fiber'
render(<mesh />, document.getElementById('canvas'), { shadows: true, ... })
- Moving to zustand for reactive internal state
// Fetches everything, renders on every change to the state model, no breaking change ...
const state = useThree()
// Fetches specific parts, renders only when these change
const camera = useThree(state => state.camera)
- Adaptive pixelratio to allow scaling down resolution on movement for expensive scenes etc
// Since zustand drives state, pixelratio is now reactive
const pixelRatio = useThree(state => state.pixelRatio)
- Opt in adaptive performance
This is an opt in feature that makes it possible to create components that could be dropped into the scene that will reduce visuals if the system is in regression, similar to what Sketchfab does (lower textures, fbo's etc on camera movement and so on).
- Object shortcuts (specifically objects that feature
setScalar
)
<mesh position={0} scale={1} rotation={Math.PI} />
- Clock control
<Canvas frameloop='always' | 'demand' | 'never'
always
, game loopdemand
, on prop changes, invalide and a forced first rendernever
, no invalidation
import { advance } from 'react-three-fiber'
// Render a single frame, we can inject custom timestamps
advance(Date.now())
- Unit testing, snapshots, expecting scene outcome, act
https://github.com/pmndrs/react-three-fiber/tree/master/packages/test-renderer
- Fix prop delete in HMR and otherwise
Removal of useUpdate
Instead, use:
const ref = useRef()
useLayoutEffect(() => ref.currrent = ..., [condition])
or
<mesh onUpdate={didUpdate} />
Removal of useResource
Instead, use:
const [material, set] = useState()
return (
<>
<meshBasicMaterial ref={material} />
{material && <Foo material={material}>}
</>
)
Changed
pixelRatio
->dpr
colorManagement={false}
->linear
noEvents={true}
->state.raycaster,.enabled = false
shadowMap
->shadows
invalidateFrameloop
->frameloop='demand'
v5.0.0 release
- Rewrite reconciler, optimize hot paths
- Inline react-reconciler (again, faster hot paths) via closure-compiler
- Remove bloat (
<Dom />
,{ __$ } = useLoader
,sRGB
- Color Management by default
- Webgl2 by default
- Auto-attach geometries and materials
<mesh>
<planeBufferGeometry />
<meshBasicMaterial />
</mesh>
- #429
viewport()
, calculates precise viewport bounds + distance
Current viewport is responsive to screen resize, that makes no sense because the viewport is reliant on the camera position. If the camera changes/moves, the last viewport is obsolete.
viewport.width/height/etc
will still work as always, but calling it as a function gives you recalculated, fresh values.
const { viewport } = useThree()
const { width, height, factor, distance } = viewport(optionalTarget)
const ref = useResource()
- Xhr and error-boundary support for useLoader
Crashes in loaders were previously swallowed, these will now throw so that you can catch then properly with error-boundaries. You also can tap into the loading process.
useLoader(
Loader,
url,
extensions, // optional
xhr => console.log((xhr.loaded / xhr.total * 100) + '% loaded'), // optional
)
- Primitives do not dispose
Primitives are objects that are created imperatively, they should not be freed or disposed of by r3f.
Previously:
return <primitive object={scene} dispose={null} />
V5:
return <primitive object={scene} />
In the unlikely case that you do want to dispose a primitive (i would be OK with not documenting that at all, primitives should normally not be touched by r3f):
return <primitive object={scene} dispose={undefined} />
- Support for contextmenu & dblclick #530
- addAfterEffects (for global before and after render effects) 0307a13
- Preloading assets
R3f uses use-asset instead of react-promise-suspense, which has better cache busting strategies and preload support.
import { useLoader } from "react-three-fiber"
useLoader.preload(GLTFLoader, url)
v4.1.0
v2.0.0
Breaking changes
- I recommend importing three from 'three' instead of 'three/src/Three', the lib pulls the former
- Name attachments have been changed, see: https://github.com/drcmda/react-three-fiber#objects-and-properties
- Events have been changed to pointer events, see: https://github.com/drcmda/react-three-fiber#events