feat(tresjs): added tresjs package with new Tresjs v5 integrated post-processing components
Fix #605 - https://github.com/Tresjs/post-processing/issues/212 Co-authored-by: Iro <155815508+Iro96@users.noreply.github.com>
This commit is contained in:
@@ -74,6 +74,7 @@ words:
|
||||
- dompurify
|
||||
- dotenvx
|
||||
- DownloadLive2DSDK
|
||||
- Downsampling
|
||||
- dreamlog
|
||||
- dtolnay
|
||||
- dtype
|
||||
@@ -157,6 +158,7 @@ words:
|
||||
- mousedown
|
||||
- mouseup
|
||||
- msvc
|
||||
- multisampling
|
||||
- Myriam
|
||||
- ndarray
|
||||
- Neko
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^5.4.1",
|
||||
"@antfu/ni": "^26.0.1",
|
||||
"@arethetypeswrong/core": "^0.18.2",
|
||||
"@electron-toolkit/eslint-config-ts": "^3.1.0",
|
||||
"@iconify/utils": "^3.0.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@proj-airi/stage-ui-three",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"description": "A collection of 3D scene components that used by Project AIRI",
|
||||
"author": {
|
||||
"name": "Moeru AI Project AIRI Team",
|
||||
@@ -24,9 +24,9 @@
|
||||
"@pixiv/three-vrm": "^3.4.2",
|
||||
"@pixiv/three-vrm-animation": "^3.4.2",
|
||||
"@pixiv/three-vrm-core": "^3.4.2",
|
||||
"@proj-airi/tresjs": "workspace:*",
|
||||
"@tresjs/cientos": "^5.0.0",
|
||||
"@tresjs/core": "^5.0.2",
|
||||
"@tresjs/post-processing": "^2.4.0",
|
||||
"@vueuse/core": "^13.9.0",
|
||||
"culori": "^4.0.2",
|
||||
"pinia": "^3.0.3",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
/*
|
||||
* ThreeScene.vue
|
||||
* - Root vue component of stage-ui-three package
|
||||
* - This scene component the root for all the sub components in the 3d scene
|
||||
* - This package, stage-ui-three, is a stateful package
|
||||
@@ -13,8 +12,8 @@ import type { DirectionalLight, SphericalHarmonics3, Texture, WebGLRenderTarget
|
||||
|
||||
import type { Vec3 } from '../stores/model-store'
|
||||
|
||||
import { EffectComposerPmndrs, HueSaturationPmndrs } from '@proj-airi/tresjs/post-processing'
|
||||
import { TresCanvas } from '@tresjs/core'
|
||||
import { EffectComposerPmndrs, HueSaturationPmndrs } from '@tresjs/post-processing'
|
||||
import { useElementBounding } from '@vueuse/core'
|
||||
import { formatHex } from 'culori'
|
||||
import { storeToRefs } from 'pinia'
|
||||
@@ -283,6 +282,7 @@ defineExpose({
|
||||
:height="height"
|
||||
:tone-mapping="ACESFilmicToneMapping"
|
||||
:tone-mapping-exposure="1"
|
||||
:clear-alpha="0"
|
||||
@ready="onTresReady"
|
||||
>
|
||||
<OrbitControls
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ vrmDialog.onChange(handleAddVRMModel)
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuContent
|
||||
class="will-change-[opacity,transform] z-10000 max-w-45 rounded-lg p-0.5 shadow-md outline-none data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=top]:animate-slideDownAndFade"
|
||||
bg="neutral-700/50 dark:neutral-950/50"
|
||||
bg="neutral-100/50 dark:neutral-950/50"
|
||||
transition="colors duration-200 ease-in-out"
|
||||
backdrop-blur-sm
|
||||
align="end"
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@proj-airi/tresjs",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"description": "Shared Tres (effects, composables)",
|
||||
"author": {
|
||||
"name": "Moeru AI Project AIRI Team",
|
||||
"email": "airi@moeru.ai",
|
||||
"url": "https://github.com/moeru-ai"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/moeru-ai/airi.git",
|
||||
"directory": "packages/tres"
|
||||
},
|
||||
"exports": {
|
||||
"./post-processing": "./src/post-processing/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "vue-tsc --noEmit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tresjs/cientos": ">=5.0.0 <6.0.0",
|
||||
"@tresjs/core": ">=5.0.0 <6.0.0",
|
||||
"postprocessing": ">=6.0.0 <7.0.0",
|
||||
"three": ">=0.180.0 <0.181.0",
|
||||
"vue": ">=3.5.0 <4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/three": "^0.180.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<!--
|
||||
https://github.com/Tresjs/post-processing/blob/c89b5b4353d889d58da4dd6746e64b32a3f711ec/src/core/pmndrs/EffectComposerPmndrs.vue
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { EffectComposer } from 'postprocessing'
|
||||
import type { WebGLRenderer } from 'three'
|
||||
import type { InjectionKey, ShallowRef } from 'vue'
|
||||
|
||||
import WEBGL from 'three/examples/jsm/capabilities/WebGL.js'
|
||||
|
||||
import { useTres, useTresContext } from '@tresjs/core'
|
||||
import {
|
||||
DepthDownsamplingPass,
|
||||
EffectComposer as EffectComposerImpl,
|
||||
NormalPass,
|
||||
RenderPass,
|
||||
} from 'postprocessing'
|
||||
import { HalfFloatType } from 'three'
|
||||
import { computed, onUnmounted, provide, shallowRef, watch } from 'vue'
|
||||
|
||||
export const effectComposerInjectionKey: InjectionKey<ShallowRef<EffectComposer | null>> = Symbol('effectComposerPmndrs')
|
||||
|
||||
export interface EffectComposerPmndrsProps {
|
||||
enabled?: boolean
|
||||
depthBuffer?: boolean
|
||||
disableNormalPass?: boolean
|
||||
stencilBuffer?: boolean
|
||||
resolutionScale?: number
|
||||
autoClear?: boolean
|
||||
multisampling?: number
|
||||
frameBufferType?: number
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = withDefaults(defineProps<EffectComposerPmndrsProps>(), {
|
||||
enabled: true,
|
||||
autoClear: true,
|
||||
frameBufferType: HalfFloatType,
|
||||
disableNormalPass: false,
|
||||
depthBuffer: undefined,
|
||||
multisampling: 0,
|
||||
stencilBuffer: undefined,
|
||||
})
|
||||
const emit = defineEmits(['render'])
|
||||
|
||||
const { renderer } = useTresContext()
|
||||
const { scene, camera, sizes } = useTres()
|
||||
|
||||
const effectComposer: ShallowRef<EffectComposerImpl | null> = shallowRef(null)
|
||||
|
||||
let downSamplingPass: DepthDownsamplingPass | null = null
|
||||
let normalPass: NormalPass | null = null
|
||||
|
||||
provide(effectComposerInjectionKey, effectComposer)
|
||||
defineExpose({ composer: effectComposer })
|
||||
|
||||
function setNormalPass() {
|
||||
if (!effectComposer.value) {
|
||||
return
|
||||
}
|
||||
|
||||
normalPass = new NormalPass(scene.value, camera.value)
|
||||
normalPass.enabled = false
|
||||
effectComposer.value.addPass(normalPass)
|
||||
if (props.resolutionScale !== undefined && WEBGL.isWebGL2Available()) {
|
||||
downSamplingPass = new DepthDownsamplingPass({
|
||||
normalBuffer: normalPass.texture,
|
||||
resolutionScale: props.resolutionScale,
|
||||
})
|
||||
|
||||
downSamplingPass.enabled = false
|
||||
effectComposer.value.addPass(downSamplingPass)
|
||||
}
|
||||
}
|
||||
|
||||
const effectComposerParams = computed(() => {
|
||||
const plainEffectComposer = new EffectComposerImpl()
|
||||
const params = {
|
||||
depthBuffer: props.depthBuffer !== undefined
|
||||
? props.depthBuffer
|
||||
: plainEffectComposer.inputBuffer.depthBuffer,
|
||||
stencilBuffer:
|
||||
props.stencilBuffer !== undefined
|
||||
? props.stencilBuffer
|
||||
: plainEffectComposer.inputBuffer.stencilBuffer,
|
||||
multisampling: WEBGL.isWebGL2Available()
|
||||
? props.multisampling !== undefined
|
||||
? props.multisampling
|
||||
: plainEffectComposer.multisampling
|
||||
: 0,
|
||||
frameBufferType: props.frameBufferType !== undefined
|
||||
? props.frameBufferType
|
||||
: HalfFloatType,
|
||||
}
|
||||
|
||||
plainEffectComposer.dispose()
|
||||
|
||||
return params
|
||||
})
|
||||
|
||||
function initEffectComposer() {
|
||||
if (!renderer && !scene.value && !camera.value) {
|
||||
return
|
||||
}
|
||||
|
||||
effectComposer.value?.dispose()
|
||||
effectComposer.value = new EffectComposerImpl(renderer.instance as WebGLRenderer, effectComposerParams.value)
|
||||
effectComposer.value.addPass(new RenderPass(scene.value, camera.value))
|
||||
|
||||
if (!props.disableNormalPass) {
|
||||
setNormalPass()
|
||||
}
|
||||
}
|
||||
|
||||
watch([scene, camera, () => props.disableNormalPass], () => {
|
||||
if (!sizes.width.value || !sizes.height.value) {
|
||||
return
|
||||
}
|
||||
|
||||
initEffectComposer()
|
||||
})
|
||||
|
||||
watch(() => [sizes.width.value, sizes.height.value], ([width, height]) => {
|
||||
// effect composer should only live once the canvas has a size > 0
|
||||
if (!width && !height) {
|
||||
return
|
||||
}
|
||||
|
||||
if (effectComposer.value) {
|
||||
effectComposer.value.setSize(width, height)
|
||||
}
|
||||
else {
|
||||
initEffectComposer()
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
renderer.replaceRenderFunction((notifySuccess) => {
|
||||
if (props.enabled && renderer.instance && effectComposer.value && sizes.width.value && sizes.height.value) {
|
||||
const currentAutoClear = renderer.instance.autoClear
|
||||
renderer.instance.autoClear = props.autoClear
|
||||
if (props.stencilBuffer && !props.autoClear) {
|
||||
renderer.instance.clearStencil()
|
||||
}
|
||||
|
||||
effectComposer.value.render()
|
||||
emit('render', effectComposer.value)
|
||||
renderer.instance.autoClear = currentAutoClear
|
||||
notifySuccess()
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
effectComposer.value?.dispose()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot />
|
||||
</template>
|
||||
@@ -0,0 +1,47 @@
|
||||
<script lang="ts" setup>
|
||||
import type { BlendFunction } from 'postprocessing'
|
||||
|
||||
import { HueSaturationEffect } from 'postprocessing'
|
||||
|
||||
import { useEffectPmndrs } from '../../composables/use-effect-pmndrs'
|
||||
import { makePropWatchers } from '../../utils'
|
||||
|
||||
export interface HueSaturationPmndrsProps {
|
||||
/**
|
||||
* The saturation adjustment. A value of 0.0 results in grayscale, and 1.0 leaves saturation unchanged.
|
||||
* Range: [0.0, 1.0]
|
||||
*/
|
||||
saturation?: number
|
||||
|
||||
/**
|
||||
* The hue adjustment in radians.
|
||||
* Range: [-π, π] (or [0, 2π] for a full rotation)
|
||||
*/
|
||||
hue?: number
|
||||
|
||||
/**
|
||||
* The blend function. Defines how the effect blends with the original scene.
|
||||
*/
|
||||
blendFunction?: BlendFunction
|
||||
}
|
||||
|
||||
const props = defineProps<HueSaturationPmndrsProps>()
|
||||
|
||||
const { pass, effect } = useEffectPmndrs(() => new HueSaturationEffect(props), props)
|
||||
|
||||
defineExpose({ pass, effect })
|
||||
|
||||
makePropWatchers(
|
||||
[
|
||||
[() => props.blendFunction, 'blendMode.blendFunction'],
|
||||
[() => props.hue, 'hue'],
|
||||
[() => props.saturation, 'saturation'],
|
||||
],
|
||||
effect,
|
||||
() => new HueSaturationEffect(),
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot />
|
||||
</template>
|
||||
@@ -0,0 +1 @@
|
||||
export { default as HueSaturationPmndrs } from './HueSaturationPmndrs.vue'
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as EffectComposerPmndrs } from './EffectComposerPmndrs.vue'
|
||||
export { HueSaturationPmndrs } from './effects'
|
||||
@@ -0,0 +1 @@
|
||||
export { useEffectPmndrs } from './use-effect-pmndrs'
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* https://github.com/Tresjs/post-processing/blob/c89b5b4353d889d58da4dd6746e64b32a3f711ec/src/core/pmndrs/EffectComposerPmndrs.vue
|
||||
*/
|
||||
|
||||
import type { Effect } from 'postprocessing'
|
||||
import type { Reactive, ShallowRef } from 'vue'
|
||||
|
||||
import { useTres } from '@tresjs/core'
|
||||
import { EffectPass } from 'postprocessing'
|
||||
import { inject, nextTick, onUnmounted, shallowRef, watch, watchEffect } from 'vue'
|
||||
|
||||
import { effectComposerInjectionKey } from '../components/EffectComposerPmndrs.vue'
|
||||
|
||||
/**
|
||||
* @param newEffectFunction - A function that returns a new effect instance.
|
||||
* @param passDependencies - A reactive object that the pass depends on (usually props). Changes to this object will trigger re-rendering.
|
||||
* @param dependencyFieldsTriggeringRecreation - fields in passDependencies that require effect recreation when changed
|
||||
*/
|
||||
export function useEffectPmndrs<T extends Effect, D extends Record<PropertyKey, any>>(newEffectFunction: () => T, passDependencies: Reactive<D>, dependencyFieldsTriggeringRecreation?: (keyof D)[]): {
|
||||
pass: ShallowRef<EffectPass | null>
|
||||
effect: ShallowRef<T | null>
|
||||
} {
|
||||
const composer = inject(effectComposerInjectionKey)
|
||||
|
||||
const pass = shallowRef<EffectPass | null>(null) as ShallowRef<EffectPass | null>
|
||||
const effect = shallowRef<T | null>(null) as ShallowRef<T | null>
|
||||
|
||||
const { scene, camera, invalidate } = useTres()
|
||||
|
||||
watch(passDependencies, () => invalidate())
|
||||
|
||||
const removePass = () => {
|
||||
if (pass.value) {
|
||||
composer?.value?.removePass(pass.value)
|
||||
}
|
||||
|
||||
effect.value?.dispose()
|
||||
pass.value?.dispose()
|
||||
}
|
||||
|
||||
const createEffect = (index?: number) => {
|
||||
if (!camera.value || !composer?.value || !scene.value) {
|
||||
return
|
||||
}
|
||||
|
||||
effect.value = newEffectFunction()
|
||||
pass.value = new EffectPass(camera.value, effect.value)
|
||||
|
||||
composer.value.addPass(pass.value, index)
|
||||
}
|
||||
|
||||
// Watch for changes in props that require effect recreation
|
||||
if (dependencyFieldsTriggeringRecreation) {
|
||||
watch(
|
||||
() => dependencyFieldsTriggeringRecreation.map(field => passDependencies[field]),
|
||||
() => {
|
||||
if (!composer?.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const index = composer.value?.passes.findIndex(p => p === pass.value)
|
||||
if (!~index) {
|
||||
return
|
||||
}
|
||||
|
||||
removePass()
|
||||
createEffect(index)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (!camera.value || !effect?.value) {
|
||||
return
|
||||
}
|
||||
|
||||
effect.value.mainCamera = camera.value
|
||||
})
|
||||
|
||||
// Initial effect creation
|
||||
const unwatch = watchEffect(() => {
|
||||
if (!camera.value || !composer?.value || !scene.value) {
|
||||
return
|
||||
}
|
||||
|
||||
nextTick(() => unwatch())
|
||||
if (effect.value) {
|
||||
return
|
||||
}
|
||||
|
||||
createEffect()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
removePass()
|
||||
})
|
||||
|
||||
return {
|
||||
pass,
|
||||
effect,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
export {
|
||||
EffectComposerPmndrs,
|
||||
HueSaturationPmndrs,
|
||||
} from './components'
|
||||
export {
|
||||
useEffectPmndrs,
|
||||
} from './composables'
|
||||
export {
|
||||
get,
|
||||
makePropWatcher,
|
||||
makePropWatchers,
|
||||
makePropWatchersUsingAllProps,
|
||||
omit,
|
||||
set,
|
||||
} from './utils'
|
||||
@@ -0,0 +1,2 @@
|
||||
export { get, omit, set } from './object'
|
||||
export { makePropWatcher, makePropWatchers, makePropWatchersUsingAllProps } from './props'
|
||||
@@ -0,0 +1,90 @@
|
||||
const pathRegex = /([^[.\]])+/g
|
||||
|
||||
/**
|
||||
* Retrieves the value at a given path within a provided object.
|
||||
*
|
||||
* @template T - The type of value to be returned
|
||||
*
|
||||
* @param {any} obj - The object to extract value from
|
||||
* @param {string | string[]} path - A path or an array of path where the value should be get from
|
||||
*
|
||||
* @returns {T | undefined} - The value at the given path in the object, or undefined if path is not found
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* const obj = { a: { b: { c: 1 } } }
|
||||
*
|
||||
* const result = get(obj, 'a.b.c')
|
||||
*
|
||||
* console.log(result) // 1
|
||||
*/
|
||||
export function get<T>(obj: any, path: string | string[]): T | undefined {
|
||||
if (!path) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const pathArray = Array.isArray(path)
|
||||
? path
|
||||
: path.match(pathRegex)
|
||||
|
||||
return pathArray?.reduce((prevObj, key) => prevObj && prevObj[key], obj)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value at a given path within a provided object. If the path does not exist, nested objects will be created.
|
||||
*
|
||||
* @param {any} obj - The original object to set value in
|
||||
* @param {string | string[]} path - A path or an array of path where the value should be set
|
||||
* @param {any} value - The value to be set at the provided path
|
||||
*
|
||||
* @returns {void}
|
||||
*
|
||||
* @example
|
||||
* const obj = { a: { b: { c: 1 } } }
|
||||
*
|
||||
* set(obj, 'a.b.c', 2)
|
||||
*
|
||||
* console.log(obj) // { a: { b: { c: 2 } } }
|
||||
*/
|
||||
export function set(obj: any, path: string | string[], value: any): void {
|
||||
const pathArray = Array.isArray(path)
|
||||
? path
|
||||
: path.match(pathRegex)
|
||||
|
||||
if (pathArray) {
|
||||
pathArray.reduce((acc, key, i) => {
|
||||
if (acc[key] === undefined) {
|
||||
acc[key] = {}
|
||||
}
|
||||
if (i === pathArray.length - 1) {
|
||||
acc[key] = value
|
||||
}
|
||||
|
||||
return acc[key]
|
||||
}, obj)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Omits given properties from a provided object.
|
||||
*
|
||||
* @template T - An object with string keys and any type of values
|
||||
*
|
||||
* @param {T} obj - The original object to omit properties from
|
||||
* @param {(keyof T)[]} properties - An array of property key names to omit from the base object
|
||||
*
|
||||
* @returns {Partial<T>} The new object with omitted properties
|
||||
*
|
||||
* @example
|
||||
* const obj = { a: 1, b: 2, c: 3 }
|
||||
* const propsToOmit = ['b', 'c']
|
||||
*
|
||||
* const newObj = omit(obj, propsToOmit)
|
||||
*
|
||||
* console.log(newObj) // { a: 1 }
|
||||
*/
|
||||
export function omit<T extends Record<string, any>>(obj: T, properties: (keyof T)[]): Partial<T> {
|
||||
const newObj = { ...obj }
|
||||
properties.forEach(prop => delete newObj[prop])
|
||||
return newObj
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { Ref, WatchOptions } from 'vue'
|
||||
|
||||
import { watch } from 'vue'
|
||||
|
||||
import { get, set } from './object'
|
||||
|
||||
/**
|
||||
* Creates a prop watcher function that monitors changes to a property and updates a target object.
|
||||
*
|
||||
* @template T - The type of the property being watched.
|
||||
* @template E - The type of the target object.
|
||||
* @param {() => T} propGetter - A function that retrieves the prop value to be watched.
|
||||
* @param {Ref<E>} target - A Ref representing the target object to be updated.
|
||||
* @param {string} propertyPath - The dot-separated path to the property within the target object.
|
||||
* @param {() => E & { dispose?(): void }} newPlainObjectFunction - A function that creates a new plain object to retrieve the defaults from with an optional "dispose" method for cleanup.
|
||||
* @param {WatchOptions} watchOptions - The options for watch.
|
||||
*/
|
||||
export function makePropWatcher<T, E>(
|
||||
propGetter: () => T,
|
||||
target: Ref<E>,
|
||||
propertyPath: string,
|
||||
newPlainObjectFunction: () => E & { dispose?: () => void },
|
||||
watchOptions: WatchOptions = {},
|
||||
) {
|
||||
return watch(propGetter, (newValue) => {
|
||||
if (!target.value) {
|
||||
return
|
||||
}
|
||||
if (newValue === undefined) {
|
||||
const plainObject = newPlainObjectFunction()
|
||||
|
||||
set(target.value, propertyPath, get(plainObject, propertyPath))
|
||||
|
||||
plainObject.dispose?.()
|
||||
}
|
||||
else {
|
||||
set(target.value, propertyPath, propGetter())
|
||||
}
|
||||
}, watchOptions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates multiple prop watchers for monitoring changes to multiple properties and updating a target object.
|
||||
*
|
||||
* @template T - The type of the property being watched.
|
||||
* @template E - The type of the target object.
|
||||
* @param {(string | (() => T))[][]} propGettersAndPropertyPaths - An array of arrays containing pairs of prop getters and their corresponding property paths within the target object.
|
||||
* @param {Ref<E>} target - A Ref representing the target object to be updated.
|
||||
* @param {() => E & { dispose?(): void }} newPlainObjectFunction - A function that creates a new plain object to retrieve the defaults from with an optional "dispose" method for cleanup.
|
||||
*/
|
||||
export function makePropWatchers<E>(
|
||||
propGettersAndPropertyPaths: (string | (() => any))[][],
|
||||
target: Ref<E>,
|
||||
newPlainObjectFunction: () => E & { dispose?: () => void },
|
||||
) {
|
||||
return propGettersAndPropertyPaths.map(([propGetterFn, path]) => makePropWatcher(
|
||||
propGetterFn as () => any,
|
||||
target,
|
||||
path as string,
|
||||
newPlainObjectFunction,
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates multiple prop watchers via the props object for monitoring changes to multiple properties and updating a target object.
|
||||
* Use this method in case the prop names match the names of the properties you want to set on your target object.
|
||||
*
|
||||
* @param props - The props object. Usually created via defineProps.
|
||||
* @param {Ref<E>} target - A Ref representing the target object to be updated.
|
||||
* @param {() => E & { dispose?(): void }} newPlainObjectFunction - A function that creates a new plain object to retrieve the defaults from with an optional "dispose" method for cleanup.
|
||||
*/
|
||||
export function makePropWatchersUsingAllProps<E>(
|
||||
props: { [key: PropertyKey]: any },
|
||||
target: Ref<E>,
|
||||
newPlainObjectFunction: () => E & { dispose?: () => void },
|
||||
) {
|
||||
return Object.keys(props).map(key => makePropWatcher(
|
||||
() => props[key],
|
||||
target,
|
||||
key,
|
||||
newPlainObjectFunction,
|
||||
))
|
||||
}
|
||||
Generated
+25
-6
@@ -79,9 +79,6 @@ importers:
|
||||
'@antfu/eslint-config':
|
||||
specifier: ^5.4.1
|
||||
version: 5.4.1(@unocss/eslint-plugin@66.5.2(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2))(@vue/compiler-sfc@3.5.22)(eslint-plugin-format@1.0.1(eslint@9.36.0(jiti@2.5.1)))(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.2)(jiti@2.5.1)(jsdom@25.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(less@4.4.1)(lightningcss@1.30.1)(msw@2.7.3(@types/node@24.5.2)(typescript@5.9.2))(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))
|
||||
'@antfu/ni':
|
||||
specifier: ^26.0.1
|
||||
version: 26.0.1
|
||||
'@arethetypeswrong/core':
|
||||
specifier: ^0.18.2
|
||||
version: 0.18.2
|
||||
@@ -1985,15 +1982,15 @@ importers:
|
||||
'@pixiv/three-vrm-core':
|
||||
specifier: ^3.4.2
|
||||
version: 3.4.2(three@0.180.0)
|
||||
'@proj-airi/tresjs':
|
||||
specifier: workspace:*
|
||||
version: link:../tresjs
|
||||
'@tresjs/cientos':
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0(@tresjs/core@5.0.2(three@0.180.0)(vue@3.5.22(typescript@5.9.2)))(@types/three@0.180.0)(react@18.3.1)(three@0.180.0)(vue@3.5.22(typescript@5.9.2))
|
||||
'@tresjs/core':
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2(three@0.180.0)(vue@3.5.22(typescript@5.9.2))
|
||||
'@tresjs/post-processing':
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0(@tresjs/core@5.0.2(three@0.180.0)(vue@3.5.22(typescript@5.9.2)))(three@0.180.0)(typescript@5.9.2)(vue@3.5.22(typescript@5.9.2))
|
||||
'@vueuse/core':
|
||||
specifier: ^13.9.0
|
||||
version: 13.9.0(vue@3.5.22(typescript@5.9.2))
|
||||
@@ -2033,6 +2030,28 @@ importers:
|
||||
specifier: ^2.15.4
|
||||
version: 2.15.4(typescript@5.9.2)
|
||||
|
||||
packages/tresjs:
|
||||
dependencies:
|
||||
'@tresjs/cientos':
|
||||
specifier: '>=5.0.0 <6.0.0'
|
||||
version: 5.0.0(@tresjs/core@5.0.2(three@0.180.0)(vue@3.5.22(typescript@5.9.2)))(@types/three@0.180.0)(react@18.3.1)(three@0.180.0)(vue@3.5.22(typescript@5.9.2))
|
||||
'@tresjs/core':
|
||||
specifier: '>=5.0.0 <6.0.0'
|
||||
version: 5.0.2(three@0.180.0)(vue@3.5.22(typescript@5.9.2))
|
||||
postprocessing:
|
||||
specifier: '>=6.0.0 <7.0.0'
|
||||
version: 6.37.8(three@0.180.0)
|
||||
three:
|
||||
specifier: '>=0.180.0 <0.181.0'
|
||||
version: 0.180.0
|
||||
vue:
|
||||
specifier: '>=3.5.0 <4.0.0'
|
||||
version: 3.5.22(typescript@5.9.2)
|
||||
devDependencies:
|
||||
'@types/three':
|
||||
specifier: ^0.180.0
|
||||
version: 0.180.0
|
||||
|
||||
packages/ui:
|
||||
dependencies:
|
||||
'@vueuse/core':
|
||||
|
||||
Reference in New Issue
Block a user