react-three/drei 慢慢翻译的中文文档

2,327 阅读13分钟

找了一圈没找到react-three/drei的中文文档,那就根据自己组件的使用频率来试着自己写写。写下来可能会不太规范.
免责声明:单纯翻译drei的GitHub的readme。大部分翻译来自翻译软件。
查看threeJs:基础概念,基本上使用都在这里。
three.js中文:一些比官方文档讲解的更直观(有免费threejs相关视频教程和一些例子)
查看react-three/drei:收集对@react three/fiber有用的助手和功能齐全的现成抽象

npm install @react-three/drei

基础用法

import { PerspectiveCamera, PositionalAudio, ... } from '@react-three/drei'

具体内容

Cameras相机

PerspectiveCamera(透视摄像机)

最常使用

OpenGL帧缓存对象(FBO:Frame Buffer Object )

type Props = Omit<JSX.IntrinsicElements['perspectiveCamera'], 'children'> & {
  /** 将相机注册为系统默认值,fiber将使用它开始渲染 */
  makeDefault?: boolean
  /** 手动将停止响应,您必须自己计算纵横比。 */
  manual?: boolean
  /** 内容将跟随相机,或者在拍摄时隐藏(如果您传递函数) */
  children?: React.ReactNode | ((texture: THREE.Texture) => React.ReactNode)
  /** 要渲染的帧数, 0 */
  frames?: number
  /** 帧缓存分辨率?? 未找到合适的翻译, 256 */
  resolution?: number
  /** 功能使用的可选环境贴图 */
  envMap?: THREE.Texture
}

一个响应式 THREE.PerspectiveCamera,可将自己设置为默认设置。

<PerspectiveCamera makeDefault {...props} />
<mesh />

您同样可以使用children,这样它将占据与摄像机相同的位置,并随着摄像机的移动而移动。

<PerspectiveCamera makeDefault {...props}>
  <mesh />
</PerspectiveCamera>

您也可以手动驱动它,但它不会响应,而且您必须自己计算长宽比。

<PerspectiveCamera manual aspect={...} onUpdate={(c) => c.updateProjectionMatrix()}>

您可以使用 PerspectiveCamera 将内容渲染到 RenderTarget 中,与 CubeCamera 类似。作为子代,您必须提供一个 render-function 函数,该函数的第一个参数是纹理。该函数的结果将不会跟随摄像机,而是在 FBO 渲染时将其设置为不可见,以避免接收纹理的网格相互干扰的问题。

<PerspectiveCamera position={[0, 0, 10]}>
  {(texture) => (
    <mesh geometry={plane}>
      <meshBasicMaterial map={texture} />
    </mesh>
  )}
</PerspectiveCamera>
OrthographicCamera(正交相机)

一个响应式 THREE.OrthographicCamera,可将它自己设置为默认设置。

<OrthographicCamera makeDefault {...props}>
  <mesh />
</OrthographicCamera>

您可以使用 OrthographicCamera 将内容渲染到目标中,它的 API 与 PerspectiveCamera 相同。

<OrthographicCamera position={[0, 0, 10]}>
  {(texture) => (
    <mesh geometry={plane}>
      <meshBasicMaterial map={texture} />
    </mesh>
  )}
</OrthographicCamera>
CubeCamera(立方相机)

THREE.CubeCamera 会将其纹理作为渲染属性返回。在渲染到内部缓冲区时,它会让子代不可见,这样它们就不会包含在反射中。 (threejs中CubeCamera定义:创建6个渲染到WebGLCubeRenderTarget的摄像机)

type Props = JSX.IntrinsicElements['group'] & {
  /** 渲染帧数,无限 */
  frames?: number
  /** 帧缓存分辨率, 256 */
  resolution?: number
  /** Camera near, 0.1 */
  near?: number
  /** Camera far, 1000 */
  far?: number
  /** 临时设置为场景背景的自定义环境贴图 */
  envMap?: THREE.Texture
  /** Custom fog that is temporarily set as the scenes fog 什么场景烟雾? */
  fog?: Fog | FogExp2
  /** 拍摄立方体时,将隐藏 CubeCamera 的内容 */
  children: (tex: Texture) => React.ReactNode
}

使用帧属性可以控制摄像机是无限渲染还是静态渲染(给定次数)。例如,如果场景中有两个静态物体,可以设置帧数={2},这样两个物体就都能在反射中 "看到 "对方,而这需要多次渲染。如果有移动的物体,可以取消设置属性,改用更小的分辨率。

<CubeCamera>
  {(texture) => (
    <mesh>
      <sphereGeometry />
      <meshStandardMaterial envMap={texture} />
    </mesh>
  )}
</CubeCamera>

Controls控制器

如果可用控件默认已启用阻尼,它们会管理自己的更新,在卸载时自行移除,并与 frameloop="demand" 画布标志兼容。它们从底层的 THREE 控件继承所有属性。它们是在所有其他 useFrames 之前运行的第一个效果,以确保其他组件可以在它们之上更改摄像机。

有些控件允许设置 makeDefault,类似于 PerspectiveCamera。这将在根存储中设置 @react-three/fiber 的控件字段。在您希望控件为人所知,且应用程序的其他部分可以对其做出响应的情况下,这将使您的工作变得更加轻松。一些 drei 控件已经考虑到了这一点,如 CameraShake、Gizmo 和 TransformControls。

<CameraControls makeDefault />
const controls = useThree((state) => state.controls)

Drei 目前导出了 OrbitControls , MapControls , TrackballControls, ArcballControls, FlyControls, DeviceOrientationControls, PointerLockControls , FirstPersonControls  CameraControls  和 FaceControls 

所有控件都会对默认摄像机做出反应。如果您的场景中有一个 <PerspectiveCamera makeDefault />,它们就会对其进行控制。如果您需要注入一个命令式摄像机或一个非默认摄像机,请使用摄像机属性: <OrbitControls camera={MyCamera} />

此外,PointerLockControls 还支持一个选择器属性,可将用于激活控件的点击事件处理程序绑定到文档以外的其他元素上(例如 "点击此处播放 "按钮)。所有与选择器属性匹配的元素都将激活控件。默认情况下,它还会将光线投射事件置于中心位置,因此网格上的常规 onPointerOver 等事件将继续有效。

CameraControls相机控制

CameraControls

<CameraControls />
type CameraControlsProps = {
  /** 要控制的摄像机,默认为camera */
  camera?: PerspectiveCamera | OrthographicCamera
  /** 要连接的 DOM 元素,默认为 `gl` 渲染器 */
  domElement?: HTMLElement
  /** 将此 CameraControls 实例作为 `controls` 引用 */
  makeDefault?: boolean
  /** 事件回调, see: https://github.com/yomotsu/camera-controls#events */
  onStart?: (e?: { type: 'controlstart' }) => void
  onEnd?: (e?: { type: 'controlend' }) => void
  onChange?: (e?: { type: 'update' }) => void
}
ScrollControls滚动控制器
type ScrollControlsProps = {
  /** 精度, default 0.00001 */
  eps?: number
  /** 水平滚动, default false (vertical) */
  horizontal?: boolean
  /** 无限滚动, default false (experimental!) */
  infinite?: boolean
  /** 定义滚动区域的长度, 每页高度:100%, default 1 */
  pages?: number
  /** 增加滚动条行程的系数, default 1 */
  distance?: number
  /** 摩擦力 以秒计算, default: 0.2 (1/5 second) */
  damping?: number
  /** maxSpeed可选地允许您钳制最大速度。如果阻尼为0.2s并且看起来正常
   * 例如,在第 1 页和第 2 页之间移动,但对于相隔很远的页面则不行,因为移动速度会非常快、
   * 那么 maxSpeed 为 0.1,会将速度限制在每秒 0.1 个单位,现在可能需要比阻尼更长的时间才能将速度限制在每秒 0.1 个单位。
   * 如果目标距离较远,则到达目标所需的时间可能比阻尼时间要长得多。 Default: Infinity无限 */
  maxSpeed?: number
  enabled?: boolean
  style?: React.CSSProperties
  children: React.ReactNode
}

滚动控件会在画布前创建一个 HTML 滚动容器。您放到 <Scroll> 控件中的所有内容都将受到影响。
您可以使用 useScroll 钩子监听滚动并对其做出反应,该钩子可为您提供有用的数据,如当前滚动偏移量(offset)、delta 值以及范围查找函数:范围、曲线和可见(rangecurve and visible)。如果你想对滚动偏移量做出反应,后一种函数就特别有用,例如,如果你想将进入或退出视图的内容淡入淡出,后一种函数就特别有用。

<ScrollControls pages={3} damping={0.1}>
  {/* 此处的画布内容不会滚动,但会接收useScroll */}
  <SomeModel />
  <Scroll>
    {/* 这里的画布内容将滚动显示 */}
    <Foo position={[0, 0, 0]} />
    <Foo position={[0, viewport.height, 0]} />
    <Foo position={[0, viewport.height * 1, 0]} />
  </Scroll>
  <Scroll html>
    {/* 这里的 DOM 内容将滚动显示 */}
    <h1>html in here (optional)</h1>
    <h1 style={{ top: '100vh' }}>second page</h1>
    <h1 style={{ top: '200vh' }}>third page</h1>
  </Scroll>
</ScrollControls>

function Foo(props) {
  const ref = useRef()
  const data = useScroll()
  useFrame(() => {
    // data.offset = current scroll position, between 0 and 1, dampened
    // data.delta = current delta, between 0 and 1, dampened

    // 当滚动条处于起始位置时为 0,直到达到滚动距离的 1/3然后增加到 1
    const a = data.range(0, 1 / 3)
    // 达到滚动距离的 1/3 时开始增加,并在达到 2/3 时达到 1。
    const b = data.range(1 / 3, 1 / 3)
    // 同上,但两端的差值均为 0.1
    const c = data.range(1 / 3, 1 / 3, 0.1)
    // 将在所选范围的 0-1-0 之间移动
    const d = data.curve(1 / 3, 1 / 3)
    // 同上,但两端的差值均为 0.1
    const e = data.curve(1 / 3, 1 / 3, 0.1)
    // 如果偏移量在范围内,则返回 true;如果不在范围内,则返回 false
    const f = data.visible(2 / 3, 1 / 3)
    // visible函数还可以接收一个参数
    const g = data.visible(2 / 3, 1 / 3, 0.1)
  })
  return <mesh ref={ref} {...props} />
}
PresentationControls演示控件

半轨道控制装置,具有弹簧物理、极地变焦和快退功能,用于演示。这些控件不会转动摄像机,但会旋转其内容。它们不会像 OrbitControl 那样在达到极限时突然停止,而是会平稳地预计停止位置。

<PresentationControls
  enabled={true} // 此项设置为 false,即可禁用控件
  global={false} // 全局旋转或拖动模型
  cursor={true} // 是否在拖动时切换光标样式
  snap={false} // 弹回中心(也可以是弹簧配置)
  speed={1} // 速度系数
  zoom={1} // 达到极轴最大值的一半时的缩放因子
  rotation={[0, 0, 0]} // 默认旋转
  polar={[0, Math.PI / 2]} // 垂直限幅
  azimuth={[-Infinity, Infinity]} // 水平限值
  config={{ mass: 1, tension: 170, friction: 26 }} // 弹簧配置
  domElement={events.connected} // 该控制器的 DOM 元素事件将附加到
>
  <mesh />
</PresentationControls>
KeyboardControls键盘控制

一个基本的键盘控制器,它将您定义的数据模型分发到useKeyboard挂钩。这是开始使用键盘输入的一种相当简单的方法。

type KeyboardControlsState<T extends string = string> = { [K in T]: boolean }

type KeyboardControlsEntry<T extends string = string> = {
  /** 操作名 */
  name: T
  /** 定义它的key,可以使用 event.key 或 event.code */
  keys: string[]
  /** 如果事件接收到keyup事件,则默认为true */
  up?: boolean
}

type KeyboardControlsProps = {
  /** 键盘按键映射 */
  map: KeyboardControlsEntry[]
  /** 所有children都可以使用KeyboardControls */
  children: React.ReactNode
  /** 可选onchange事件 */
  onChange: (name: string, pressed: boolean, state: KeyboardControlsState) => void
  /** 可选事件源 */
  domElement?: HTMLElement
}

首先,将应用程序或场景包装到<KeyboardControls>中。

enum Controls {
  forward = 'forward',
  back = 'back',
  left = 'left',
  right = 'right',
  jump = 'jump',
}
function App() {
  const map = useMemo<KeyboardControlsEntry<Controls>[]>(()=>[
    { name: Controls.forward, keys: ['ArrowUp', 'KeyW'] },
    { name: Controls.back, keys: ['ArrowDown', 'KeyS'] },
    { name: Controls.left, keys: ['ArrowLeft', 'KeyA'] },
    { name: Controls.right, keys: ['ArrowRight', 'KeyD'] },
    { name: Controls.jump, keys: ['Space'] },
  ], [])
  return (
    <KeyboardControls map={map}>
      <App />
    </KeyboardControls>

您可以响应输入,它使用zustand(带有subscribeWithSelector中间件),因此所有规则都适用:

function Foo() {
  const forwardPressed = useKeyboardControls<Controls>(state => state.forward)

或者通过 subscribe(这是一个返回取消订阅函数的函数,因此你可以将它与 useEffect 搭配使用以进行清理)或 get(非反应性地获取新状态)进行瞬时处理。

function Foo() {
  const [sub, get] = useKeyboardControls<Controls>()

  useEffect(() => {
    return sub(
      (state) => state.forward,
      (pressed) => {
        console.log('forward', pressed)
      }
    )
  }, [])

  useFrame(() => {
    // 从存储中获取新数据
    const pressed = get().back
  })
}
FaceControls面部控制

相机跟随你的脸。

MotionPathControls运动路径控制

Gizmos小控件

GizmoHelper控件助手

由可视化和控制相机位置的小部件使用。
其中包括两个示例Gizmo:Gizmo Viewport和Gizmo Viewcube,使用useGizmoContext可以轻松创建自己的Gizmo。
确保在控件上设置makeDefault参数,在这种情况下,不必定义onTarget和onUpdate参数。

<GizmoHelper
  alignment="bottom-right" // 场景中的小组件对齐
  margin={[80, 80]} // 组件边距 (X, Y)
  onUpdate={/* 在摄影机动画期间调用  */}
  onTarget={/* 将当前摄影机目标(例如,从轨道控制)返回到中心动画 */}
  renderPriority={/* 使用renderPriority可以防止辅助对象在存在其他辅助对象时消失 useFrame(..., 1)*/}
>
  <GizmoViewport axisColors={['red', 'green', 'blue']} labelColor="black" />
  {/* alternative: <GizmoViewcube /> */}
</GizmoHelper>
PivotControls数据透视控件

demo

用于旋转和平移对象的控件。这些控件将粘贴到变换的对象上,并通过偏移或锚定形成一个轴。此控件具有用于某些转换的HTML注释,并支持拖动时四舍五入值的[tab]。

type PivotControlsProps = {
  /** 组件的比例, 1 */
  scale?: number
  /** 组件线的宽度, 这是THREE.Line2参数, 2.5 */
  lineWidth?: number
  /** 如果固定为 true,则大小保持不变,缩放比例以像素为单位,否则为 false。 */
  fixed?: boolean
  /** 数据透视不作为一个组,它不会移动内容,但可以在位置上偏移 */
  offset?: [number, number, number]
  /** 初始角度 */
  rotation?: [number, number, number]
  /** 起始矩阵 */
  matrix?: THREE.Matrix4
  /** 锚点,如BBAnchor,每个轴可以在 -1/0/+1 */
  anchor?: [number, number, number]
  /** 如果autoTransform为true,则拖动时自动应用局部变换, true */
  autoTransform?: boolean
  /** 允许您关闭各个轴 */
  activeAxes?: [boolean, boolean, boolean]
  /** RGB colors */
  axisColors?: [string | number, string | number, string | number]
  /** 悬停的颜色 */
  hoveredColor?: string | number
  /** HTML值注释, default: false */
  annotations?: boolean
  /** 应用于HTML注释的CSS类名 */
  annotationsClass?: string
  /**拖动启动事件 */
  onDragStart?: () => void
  /** 拖动事件 */
  onDrag?: (l: THREE.Matrix4, deltaL: THREE.Matrix4, w: THREE.Matrix4, deltaW: THREE.Matrix4) => void
  /** 拖动结束事件 */
  onDragEnd?: () => void
  /** 如果希望组件通过面可见,请将此设置为false */
  depthTest?: boolean
  opacity?: number
  visible?: boolean
  userData?: { [key: string]: any }
  children?: React.ReactNode
}
<PivotControls>
  <mesh />
</PivotControls>

您可以使用Pivot作为受控组件,在这种情况下关闭autoTransform,现在您自己负责应用矩阵变换。您也可以保持“自动变换”(autoTransform)处于启用状态,并将矩阵应用于外部对象,在这种情况下,Pivot将能够控制未在其中建立父子关系的对象。

const matrix = new THREE.Matrix4()
return (
  <PivotControls
    ref={ref}
    matrix={matrix}
    autoTransform={false}
    onDrag={({ matrix: matrix_ }) => matrix.copy(matrix_)}
DragControls拖拽控制

storybook Dom only

您可以使用 DragControls 在场景中拖动对象。它支持将拖动锁定到特定轴、设置拖动限制以及自定义拖动开始、拖动和拖动结束事件。

type DragControlsProps = {
  /** 如果自动变换为 true,拖动时会自动应用局部变换, true */
  autoTransform?: boolean
  /** 控制矩阵 */
  matrix?: THREE.Matrix4
  /** 将拖动锁定在特定轴上 */
  axisLock?: 'x' | 'y' | 'z'
  /** 限制 */
  dragLimits?: [[number, number] | undefined, [number, number] | undefined, [number, number] | undefined]
  /** 悬停事件 */
  onHover?: (hovering: boolean) => void
  /** 拖拽开始事件 */
  onDragStart?: (origin: THREE.Vector3) => void
  /** 拖拽事件 */
  onDrag?: (
    localMatrix: THREE.Matrix4,
    deltaLocalMatrix: THREE.Matrix4,
    worldMatrix: THREE.Matrix4,
    deltaWorldMatrix: THREE.Matrix4
  ) => void
  /** 拖拽结束事件 */
  onDragEnd?: () => void
  children: React.ReactNode
}
<DragControls>
  <mesh />
</DragControls>

您可以通过关闭自动变换功能将 DragControls 作为受控组件使用,这样就需要手动管理矩阵变换。另外,如果启用自动变换功能,则可以将矩阵应用于外部对象,从而使 DragControls 能够管理非直接父对象的对象。

const matrix = new THREE.Matrix4()
return (
  <DragControls
    ref={ref}
    matrix={matrix}
    autoTransform={false}
    onDrag={(localMatrix) => matrix.copy(localMatrix)}
TransformControls变换控件

一个 THREE.TransformControls的抽象概念.
您可以对对象进行包装,然后让其接收一个变换小组件。

<TransformControls mode="translate">
  <mesh />
</TransformControls>

您也可以引用对象,这样可能更容易交换目标。现在,对象不必属于同一个子图。引用可以是普通对象或 React.MutableRefObjects。

<TransformControls mode="translate" />
<OrbitControls makeDefault />
Grid网格

Demo

以 Y-up 为导向、基于着色器的网格实现。

export type GridMaterialType = {
  /** 单元格大小, default: 0.5 */
  cellSize?: number
  /** 单元厚度, default: 0.5 */
  cellThickness?: number
  /** 单元颜色, default: black */
  cellColor?: THREE.ColorRepresentation
  /** 切片尺寸, default: 1 */
  sectionSize?: number
  /** 切片厚度, default: 1 */
  sectionThickness?: number
  /** 切片颜色, default: #2080ff */
  sectionColor?: THREE.ColorRepresentation
  /** 跟随摄像机, default: false */
  followCamera?: boolean
  /** 无限显示网格, default: false */
  infiniteGrid?: boolean
  /** 淡入淡出距离, default: 100 */
  fadeDistance?: number
  /** 衰减强度, default: 1 */
  fadeStrength?: number
}

export type GridProps = GridMaterialType & {
  /** 默认平面几何参数 */
  args?: ConstructorParameters<typeof THREE.PlaneGeometry>
}
<Grid />
useHelper

一个钩子,用于向场景中的现有节点快速添加辅助对象。它在卸载时处理帮助程序的删除,并在默认情况下自动更新它。

const mesh = useRef()
useHelper(mesh, BoxHelper, 'cyan')
useHelper(condition && mesh, BoxHelper, 'red') // 可以传递false而不是对象ref来隐藏辅助对象

<mesh ref={mesh} ... />
Helper

用于以声明方式将辅助对象添加到场景中现有节点的组件。它在卸载时处理帮助程序的删除,并在默认情况下自动更新它。

<mesh>
  <boxGeometry />
  <meshBasicMaterial />

  <Helper type={BoxHelper} args={['royalblue']} />
  <Helper type={VertexNormalsHelper} args={[1, 0xff0000]} />
</mesh>

Misc(杂项)

Html

用于将HTML内容绑定到场景的任何对象。它将自动投影到物体的行踪

<Html
  as='div' // 包装元素 (默认: 'div')
  wrapperClass // 包装元素的 className (默认: undefined)
  prepend // 在画布后面投影内容 (默认值: false)
  center // 添加 -50%/-50% css 变换(默认值:false)[在变换模式下忽略]
  fullscreen // 对齐到左上角,填充屏幕 (default:false) [在变换模式下忽略]
  distanceFactor={10} // 如果设置(默认值:undefined),则子项将按此系数缩放,并且还会按 OrthographicCamera 缩放到 PerspectiveCamera 的距离/缩放。
  zIndexRange={[100, 0]} // Z-order 范围 (default=[16777271, 0])
  portal={domnodeRef} // 对目标容器的引用 (default=undefined)
  transform // 如果为 true,则应用 matrix3d 转换 (default=false)
  sprite // 渲染为 sprite,但仅在变换模式下 (default=false)
  calculatePosition={(el: Object3D, camera: Camera, size: { width: number; height: number }) => number[]} // 覆盖默认定位功能。(默认 = 未定义)[在变换模式下忽略]
  occlude={[ref]} // 可以是 true 或 Ref<Object3D>[],true 会遮挡整个场景(默认值:undefined)
  onOcclude={(hidden) => null} // 可见性变化时的回调(默认:undefined)
  {...groupProps} // 全部THREE.Group props有效
  {...divProps} // 全部HTMLDivElement props有效
>
  <h1>hello</h1>
  <Grid cols={4}>world</Grid>
</Html>

当 Html 对象隐藏时,它会在最里面的 div 上设置 opacity 属性。如果您想自己制作动画或控制过渡,则可以使用 。onOcclude

const [hidden, set] = useState() 
<Html 
occlude 
onOcclude={set} 
style={{ 
transition: 'all 0.5s', 
opacity: hidden ? 0 : 1, 
transform: `scale(${hidden ? 0.5 : 1})` 
}} 
/> 

Blending occlusion

使用此模式,Html 可以隐藏在几何图形后面,就像它是 3D 场景的一部分一样。可以通过 用作 prop 来启用它。"blending" occlude

// 启用真实遮挡
<Html occlude="blending" />

你也可以使用 prop 为 HTML 材质属性。material

<Html occlude 
    material={ 
        <meshPhysicalMaterial 
            side={DoubleSide} // Required 
            opacity={0.1} // Degree of influence of lighting on the HTML 
            ... // Any other material properties 
/> 
} 
/>