-
-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Align): add component, demo, docs (#544)
Co-authored-by: alvarosabu <[email protected]>
- Loading branch information
1 parent
e71cdad
commit e3ee52c
Showing
8 changed files
with
283 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<script setup lang="ts"> | ||
import { Align, OrbitControls } from '@tresjs/cientos' | ||
import { TresCanvas } from '@tresjs/core' | ||
</script> | ||
|
||
<template> | ||
<TresCanvas clear-color="#222"> | ||
<TresPerspectiveCamera /> | ||
<OrbitControls /> | ||
|
||
<TresAxesHelper :scale="2" /> | ||
|
||
<Align top right back> | ||
<TresMesh> | ||
<TresBoxGeometry /> | ||
<TresMeshNormalMaterial /> | ||
</TresMesh> | ||
</Align> | ||
</TresCanvas> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Align | ||
|
||
<DocsDemo> | ||
<AlignDemo /> | ||
</DocsDemo> | ||
|
||
Calculates a bounding box around its children and aligns them as a group within their parent. The component measures its contents and realigns on every frame unless `cacheKey` is set. | ||
|
||
## Usage | ||
|
||
<<< @/.vitepress/theme/components/AlignDemo.vue | ||
|
||
## Props | ||
|
||
All props are optional. | ||
|
||
| Prop | Description | | ||
| ------------ | ----------------------------------- | | ||
| `top` | If `true`, aligns bounding box bottom to `0` on the y-axis | | ||
| `bottom` | If `true`, aligns bounding box top to `0` on the y-axis. | | ||
| `left` | If `true`, aligns bounding box right to `0` on the x-axis. | | ||
| `right` | If `true`, aligns bounding box left to `0` on the x-axis. | | ||
| `front` | If `true`, aligns bounding box back to `0` on the z-axis. | | ||
| `back` | If `true`, aligns bounding box front to `0` on the z-axis. | | ||
| `disable` | If `true`, disables alignment on all axes. | | ||
| `disableX` | If `true`, disables alignment on the x-axis. | | ||
| `disableY` | If `true`, disables alignment on the y-axis. | | ||
| `disableZ` | If `true`, disables alignment on the z-axis. | | ||
| `precise` | See [Box3.setFromObject](https://threejs.org/docs/index.html?q=box3#api/en/math/Box3.setFromObject). | | ||
| `onAlign` | Callback that fires when updating, after measurement. | | ||
| `cacheKey` | If set, component will only update when `cacheKey`'s value changes. If unset, component will update every frame. | | ||
|
||
## `OnAlignCallbackProps` | ||
|
||
```ts | ||
export interface OnAlignCallbackProps { | ||
/** The next parent above <Align /> */ | ||
parent: Object3D | ||
/** The outmost container group of the <Align /> component */ | ||
container: Object3D | ||
width: number | ||
height: number | ||
depth: number | ||
boundingBox: Box3 | ||
boundingSphere: Sphere | ||
center: Vector3 | ||
verticalAlignment: number | ||
horizontalAlignment: number | ||
depthAlignment: number | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<script setup lang="ts"> | ||
import { Align, OrbitControls } from '@tresjs/cientos' | ||
import { TresCanvas } from '@tresjs/core' | ||
import { TresLeches, useControls } from '@tresjs/leches' | ||
import '@tresjs/leches/styles' | ||
const elapsed = shallowRef(0) | ||
let intervalId: ReturnType<typeof setInterval> | ||
onMounted(() => { | ||
intervalId = setInterval(() => { | ||
elapsed.value += 1000 / 30 | ||
}, 1000 / 30) | ||
}) | ||
onUnmounted(() => { | ||
clearInterval(intervalId) | ||
}) | ||
const ctrl = useControls({ | ||
top: false, | ||
left: false, | ||
front: false, | ||
bottom: false, | ||
right: false, | ||
back: false, | ||
disable: false, | ||
disableX: false, | ||
disableY: false, | ||
disableZ: false, | ||
useCacheKey: false, | ||
vIf: true, | ||
}) | ||
</script> | ||
|
||
<template> | ||
<TresLeches /> | ||
<TresCanvas> | ||
<TresPerspectiveCamera :position="[0, 0, 5]" /> | ||
<OrbitControls /> | ||
<TresAxesHelper :scale="2" :position="[0, 0, 0]" /> | ||
<Align | ||
v-if="ctrl.vIf.value.value" | ||
:left="ctrl.left.value.value" | ||
:right="ctrl.right.value.value" | ||
:top="ctrl.top.value.value" | ||
:bottom="ctrl.bottom.value.value" | ||
:front="ctrl.front.value.value" | ||
:back="ctrl.back.value.value" | ||
:disable="ctrl.disable.value.value" | ||
:disable-x="ctrl.disableX.value.value" | ||
:disable-y="ctrl.disableY.value.value" | ||
:disable-z="ctrl.disableZ.value.value" | ||
:cache-key="ctrl.useCacheKey.value.value ? (() => Math.floor(elapsed * 0.001)) : undefined" | ||
> | ||
<TresMesh :position-x="Math.cos(elapsed * 0.001) * 2" :position-y="Math.sin(elapsed * 0.001) * 2" :scale-z="Math.cos(elapsed * 0.001) * 2"> | ||
<TresBoxGeometry /> | ||
<TresMeshNormalMaterial /> | ||
</TresMesh> | ||
</Align> | ||
</TresCanvas> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
<script setup lang="ts"> | ||
import { useLoop } from '@tresjs/core' | ||
import type { Group, Object3D } from 'three' | ||
import { Box3, Sphere, Vector3 } from 'three' | ||
import type { MaybeRefOrGetter } from 'vue' | ||
import { shallowRef, toValue, watchEffect } from 'vue' | ||
// NOTE: Sources | ||
// https://github.com/pmndrs/drei/blob/master/src/core/Center.tsx | ||
export interface OnAlignCallbackProps { | ||
/** The next parent above <Align /> */ | ||
parent: Object3D | ||
/** The outmost container group of the <Align/> component */ | ||
container: Object3D | ||
width: number | ||
height: number | ||
depth: number | ||
boundingBox: Box3 | ||
boundingSphere: Sphere | ||
center: Vector3 | ||
verticalAlignment: number | ||
horizontalAlignment: number | ||
depthAlignment: number | ||
} | ||
export interface AlignProps { | ||
top?: boolean | ||
right?: boolean | ||
bottom?: boolean | ||
left?: boolean | ||
front?: boolean | ||
back?: boolean | ||
/** Disable all axes */ | ||
disable?: boolean | ||
/** Disable x-axis alignment */ | ||
disableX?: boolean | ||
/** Disable y-axis alignment */ | ||
disableY?: boolean | ||
/** Disable z-axis alignment */ | ||
disableZ?: boolean | ||
/** See https://threejs.org/docs/index.html?q=box3#api/en/math/Box3.setFromObject */ | ||
precise?: boolean | ||
/** Callback, fires when updating, after measurement */ | ||
onAlign?: (props: OnAlignCallbackProps) => void | ||
/** Optional cacheKey to keep the component from recalculating on every render */ | ||
cacheKey?: MaybeRefOrGetter<any> | ||
} | ||
const props = withDefaults(defineProps<AlignProps>(), { | ||
precise: true, | ||
cacheKey: undefined, | ||
}) | ||
const ref = shallowRef<Group>() | ||
const outer = shallowRef<Group>() | ||
const inner = shallowRef<Group>() | ||
const box3 = new Box3() | ||
const center = new Vector3() | ||
const sphere = new Sphere() | ||
function update() { | ||
if (!outer.value || !inner.value || !ref.value) { return } | ||
outer.value.matrixWorld.identity() | ||
box3.setFromObject(inner.value, props.precise) | ||
const width = box3.max.x - box3.min.x | ||
const height = box3.max.y - box3.min.y | ||
const depth = box3.max.z - box3.min.z | ||
box3.getCenter(center) | ||
box3.getBoundingSphere(sphere) | ||
const yAlign = props.top ? height / 2 : props.bottom ? -height / 2 : 0 | ||
const xAlign = props.left ? -width / 2 : props.right ? width / 2 : 0 | ||
const zAlign = props.front ? depth / 2 : props.back ? -depth / 2 : 0 | ||
outer.value.position.set( | ||
props.disable || props.disableX ? 0 : -center.x + xAlign, | ||
props.disable || props.disableY ? 0 : -center.y + yAlign, | ||
props.disable || props.disableZ ? 0 : -center.z + zAlign, | ||
) | ||
// Only fire onCentered if the bounding box has changed | ||
if (typeof props.onAlign !== 'undefined') { | ||
props.onAlign({ | ||
parent: ref.value.parent!, | ||
container: ref.value, | ||
width, | ||
height, | ||
depth, | ||
boundingBox: box3, | ||
boundingSphere: sphere, | ||
center, | ||
verticalAlignment: yAlign, | ||
horizontalAlignment: xAlign, | ||
depthAlignment: zAlign, | ||
}) | ||
} | ||
} | ||
let off: (() => void) | null = null | ||
let cacheKey: any = null | ||
// NOTE: `useLoop` requires TresContext, which is only | ||
// availble in setup. We need a handle in useEffect, | ||
// so keep a reference. | ||
const loop = useLoop() | ||
watchEffect(() => { | ||
// NOTE: update whenever cachekey changes. | ||
// NOTE: We might already always be updating, so `off`. | ||
off?.() | ||
off = null | ||
// NOTE: Don't update if we resolve to the previous cacheKey value, | ||
// unless cacheKey is null or undefined. | ||
const nextKey = toValue(props.cacheKey) | ||
if (nextKey === cacheKey && cacheKey !== null && cacheKey !== undefined) { return } | ||
cacheKey = nextKey | ||
if (props.cacheKey === null || props.cacheKey === undefined) { | ||
off = loop.onBeforeRender(() => { | ||
update() | ||
}).off | ||
} | ||
else { | ||
update() | ||
} | ||
}) | ||
defineExpose({ instance: ref, update }) | ||
</script> | ||
|
||
<template> | ||
<TresGroup ref="ref"> | ||
<TresGroup ref="outer"> | ||
<TresGroup ref="inner"> | ||
<slot></slot> | ||
</TresGroup> | ||
</TresGroup> | ||
</TresGroup> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters