# 组合式函数
Vue 3的[Composition API](https://vuejs.org/guide/extras/composition-api-faq.html#what-is-composition-api) 允许您创建可在组件之间共享的可重用逻辑。它还允许您创建可在组件中使用的自定义钩子。
**TresJS** 充分利用这个API创建了一组组合式函数,可用于创建动画、与场景交互等。它还允许您创建更复杂的场景,不仅使用Vue组件(纹理、加载器等)实现。
**TresJS** 核心在内部使用这些组合式函数,因此可以使用与核心相同API。例如,需要在内部渲染循环中更新的组件使用 `useRenderLoop` 来注册一个回调函数,每当渲染器更新场景时都会调用该函数。
## useRenderLoop
`useRenderLoop` 是 **TresJS** 动画的核心。它可以注册一个回调函数,该函数将在原生刷新率下被调用。这是 **TresJS** 中最重要的组合式函数。
```ts
const { onLoop, resume } = useRenderLoop()
onLoop(({ delta, elapsed, clock }) => {
// 它将在每一帧运行 ~60FPS (取决于您的显示器)
})
```
::: warning
请注意使用此组合式函数的性能影响。它将在每一帧运行,因此如果在回调中有大量逻辑,可能会影响应用程序的性能。特别是如果您正在更新响应式状态或引用。
:::
`onLoop` 回调接收一个基于[THREE clock](https://threejs.org/docs/?q=clock#api/en/core/Clock)的对象,该对象具有以下属性:
- `delta`: 当前帧与上一帧之间的时间差。这是自上一帧以来的时间(以秒为单位)。
- `elapsed`: 自渲染循环开始以来的时间。
这个组合式函数基于 [vueuse](https://vueuse.org/core/useRafFn/) 中的 `useRafFn` 。感谢 [@wheatjs](https://github.com/orgs/Tresjs/people/wheatjs) 的出色贡献
### 渲染前后
您还可以注册一个在渲染器更新场景之前和之后调用的回调函数。例如,添加了性能分析工具以测量FPS,将非常有用。
```ts
const { onBeforeLoop, onAfterLoop } = useRenderLoop()
onBeforeLoop(({ delta, elapsed }) => {
// 在渲染器更新场景之前运行
fps.begin()
})
onAfterLoop(({ delta, elapsed }) => {
// 在渲染器更新场景之后运行
fps.end()
})
```
### 暂停和恢复
您可以使用 `pause` 和 `resume` 方法来暂停和恢复循环渲染。
```ts
const { pause, resume } = useRenderLoop()
// 暂停循环渲染
pause()
// 恢复循环渲染
resume()
```
您还可以使用 `isActive` 属性获取循环渲染的活动状态。
```ts
const { resume, isActive } = useRenderLoop()
console.log(isActive) // false
resume()
console.log(isActive) // true
```
## useLoader
`useLoader` 组合式函数可以使用 [THREE.js loaders](https://threejs.org/docs/#manual/en/introduction/Loading-3D-models) 加载器加载资源。它返回一个带有加载后资源的Promise。
```ts
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader'
const { scene } = await useLoader(THREE.GLTFLoader, 'path/to/asset.gltf')
```
由于 `useLoader` 组合式函数返回一个Promise,您可以使用 `async/await` 或 `then/catch`。如果您在组件上使用它,请确保将其包装在 `Suspense` 组件中。有关更多信息,请参阅[Suspense](https://vuejs.org/guide/built-ins/suspense.html#suspense)。
```vue
```
## useTexture
`useTexture` 组合式函数可以使用 [THREE.js texture loader](https://threejs.org/docs/#api/en/loaders/TextureLoader) 纹理加载器加载纹理。它返回一个带有已加载纹理的Promise。
```ts
const texture = await useTexture(['path/to/texture.png'])
```
**useTexture** 还可以传入一个包含以下属性的对象:
- `map`: 用于物体表面的基本纹理
- `displacementMap`: 用于在物体表面添加凹凸或凹痕的纹理
- `normalMap`: 用于在物体上添加表面细节和阴影变化的纹理
- `roughnessMap`: 用于在物体表面添加粗糙度或哑光效果的纹理
- `metalnessMap`: 用于在物体表面添加金属效果的纹理
- `aoMap`: 用于在物体上添加环境遮蔽(在光被其他物体挡住的区域中添加阴影)的纹理
- `alphaMap`: 用于向物体添加 alpha(黑色部分渲染为透明)的纹理。在材质上使用这个映射,需要设置 `:transparent="true"`
- `matcap`: 材质颜色和阴影的纹理编码
在这种情况下,它将返回一个包含已加载纹理的对象。
```ts
const { map, displacementMap, normalMap, roughnessMap, metalnessMap, aoMap, alphaMap, matcap } = await useTexture({
map: 'path/to/albedo.png',
displacementMap: 'path/to/height.png',
normalMap: 'path/to/normal.png',
roughnessMap: 'path/to/roughness.png',
metalnessMap: 'path/to/metalness.png',
aoMap: 'path/to/ambien-occlusion.png',
alphaMap: 'path/to/alpha.png',
matcap: 'path/to/matcap.png',
})
```
然后,可以将纹理绑定到材质上。
```vue
```
与上述类似,`useTexture` 组合式函数返回一个Promise,您可以使用 `async/await` 或 `then/catch`。如果您在组件上使用它,请确保将其包装在 `Suspense` 组件中。
## useSeek
`useSeek` 组合式函数提供了一些实用工具,可轻松遍历和浏览复杂的ThreeJS场景和对象子图。它导出了4个函数,允许您根据特定属性查找子对象。
```ts
const { seek, seekByName, seekAll, seekAllByName } = useSeek()
```
`useSeek` 函数接受三个参数:
- `parent`: 一个 ThreeJS 场景或 Object3D
- `property`: 用于搜索条件的属性
- `value`: 匹配的属性值
`seek` 和 `seekByName` 函数遍历对象并返回具有指定属性和值的子对象。如果找不到具有给定属性和值的子对象,则返回 null 并抛出警告。
```ts
const carRef = ref(null)
watch(carRef, ({ model }) => {
if (model) {
const car = model.children[0]
const body = seek(car, 'name', 'Octane_Octane_Body_0')
body.color.set(new Color('blue'))
}
})
```
类似地,`seekAll` 和 `seekAllByName` 函数返回一个包含具有指定属性和值的子对象的数组。如果没有找到匹配项,则返回一个空数组,并抛出警告。
```ts
const character = ref(null)
watch(character, ({ model }) => {
if (model) {
const bones = seekAll(character, type, 'Bone')
}
})
```
## useTresContext
这个组合式函数提供对包含多个有用属性的状态模型的访问。
```ts
const { camera, renderer, camera, cameras } = useTresContext()
```
::: warning
`useTresContext` 只能在 `TresCanvas` 内部使用,因为 `TresCanvas` 充当上下文数据的提供者。如果在TresCanvas的父组件中需要上下文,请使用[TresCanvas暴露的上下文](tres-canvas#exposed-public-properties)。
:::
```vue
```
```vue
// MyModel.vue
```
### 上下文的属性
| 属性 | 描述 |
| --- | --- |
| **camera** | 当前激活的相机 |
| **cameras** | 场景中存在的相机|
| **controls** | 场景控件 |
| **deregisterCamera** | 用于取消注册相机的方法。仅在手动创建相机时需要。`template` 中的相机会自动取消注册。 |
| **extend** | 扩展组件目录。请查看 [extending](/advanced/extending) |
| **raycaster** | 用于鼠标事件的全局光线投射器 |
| **registerCamera** | 用于注册相机的方法。只有在手动创建相机时才需要。在 `template` 中,相机会自动注册。 |
| **renderer** | 场景中的 [WebGLRenderer](https://threejs.org/docs/#api/en/renderers/WebGLRenderer) |
| **scene** | [场景](https://threejs.org/docs/?q=sce#api/en/scenes/Scene) |
| **setCameraActive** | 设置当前激活的相机 |
| **sizes** | 画布的宽度、高度和宽高比 |