53 lines
1.6 KiB
JavaScript
53 lines
1.6 KiB
JavaScript
import * as React from 'react';
|
|
import { Object3D, AnimationMixer } from 'three';
|
|
import { useFrame } from '@react-three/fiber';
|
|
|
|
function useAnimations(clips, root) {
|
|
const ref = React.useRef(null);
|
|
const [actualRef] = React.useState(() => root ? root instanceof Object3D ? {
|
|
current: root
|
|
} : root : ref);
|
|
const [mixer] = React.useState(() => new AnimationMixer(undefined));
|
|
React.useLayoutEffect(() => {
|
|
if (root) actualRef.current = root instanceof Object3D ? root : root.current;
|
|
mixer._root = actualRef.current;
|
|
});
|
|
const lazyActions = React.useRef({});
|
|
const api = React.useMemo(() => {
|
|
const actions = {};
|
|
clips.forEach(clip => Object.defineProperty(actions, clip.name, {
|
|
enumerable: true,
|
|
get() {
|
|
if (actualRef.current) {
|
|
return lazyActions.current[clip.name] || (lazyActions.current[clip.name] = mixer.clipAction(clip, actualRef.current));
|
|
}
|
|
},
|
|
configurable: true
|
|
}));
|
|
return {
|
|
ref: actualRef,
|
|
clips,
|
|
actions,
|
|
names: clips.map(c => c.name),
|
|
mixer
|
|
};
|
|
}, [clips]);
|
|
useFrame((state, delta) => mixer.update(delta));
|
|
React.useEffect(() => {
|
|
const currentRoot = actualRef.current;
|
|
return () => {
|
|
// Clean up only when clips change, wipe out lazy actions and uncache clips
|
|
lazyActions.current = {};
|
|
mixer.stopAllAction();
|
|
Object.values(api.actions).forEach(action => {
|
|
if (currentRoot) {
|
|
mixer.uncacheAction(action, currentRoot);
|
|
}
|
|
});
|
|
};
|
|
}, [clips]);
|
|
return api;
|
|
}
|
|
|
|
export { useAnimations };
|