UNPKG

@react-three/drei

Version:

useful add-ons for react-three-fiber

122 lines (118 loc) 4.24 kB
import _extends from '@babel/runtime/helpers/esm/extends'; import * as THREE from 'three'; import * as React from 'react'; import { useThree, useInstanceHandle, useFrame } from '@react-three/fiber'; import { misc, easing } from 'maath'; const isObject3DRef = ref => (ref == null ? void 0 : ref.current) instanceof THREE.Object3D; const MotionContext = /* @__PURE__ */React.createContext(null); function useMotion() { const context = React.useContext(MotionContext); if (!context) throw new Error('useMotion hook must be used in a MotionPathControls component.'); return context; } function Debug({ points = 50, color = 'black' }) { const { path } = useMotion(); const [dots, setDots] = React.useState([]); const material = React.useMemo(() => new THREE.MeshBasicMaterial({ color: color }), [color]); const geometry = React.useMemo(() => new THREE.SphereGeometry(0.025, 16, 16), []); const last = React.useRef([]); React.useEffect(() => { if (path.curves !== last.current) { setDots(path.getPoints(points)); last.current = path.curves; } }); return dots.map((item, index) => /*#__PURE__*/React.createElement("mesh", { key: index, material: material, geometry: geometry, position: [item.x, item.y, item.z] })); } const MotionPathControls = /* @__PURE__ */React.forwardRef(({ children, curves = [], debug = false, debugColor = 'black', object, focus, loop = true, offset = undefined, smooth = false, eps = 0.00001, damping = 0.1, focusDamping = 0.1, maxSpeed = Infinity, ...props }, fref) => { const { camera } = useThree(); const ref = React.useRef(null); const pos = React.useRef(offset !== null && offset !== void 0 ? offset : 0); const path = React.useMemo(() => new THREE.CurvePath(), []); const state = React.useMemo(() => ({ focus, object: (object == null ? void 0 : object.current) instanceof THREE.Object3D ? object : { current: camera }, path, current: pos.current, offset: pos.current, point: new THREE.Vector3(), tangent: new THREE.Vector3(), next: new THREE.Vector3() }), [focus, object]); const instanceRef = useInstanceHandle(ref); React.useLayoutEffect(() => { const instance = instanceRef.current; path.curves = []; const _curves = curves.length > 0 ? curves : instance.children.map(instance => instance.object); for (let i = 0; i < _curves.length; i++) path.add(_curves[i]); // Smoothen curve if (smooth) { const points = path.getPoints(typeof smooth === 'number' ? smooth : 1); const catmull = new THREE.CatmullRomCurve3(points); path.curves = [catmull]; } path.updateArcLengths(); }); React.useImperativeHandle(fref, () => Object.assign(ref.current, { motion: state }), [state]); React.useLayoutEffect(() => { // When offset changes, normalise pos to avoid overshoot spinning pos.current = misc.repeat(pos.current, 1); }, [offset]); const vec = React.useMemo(() => new THREE.Vector3(), []); useFrame((_state, delta) => { const lastOffset = state.offset; easing.damp(pos, 'current', offset !== undefined ? offset : state.current, damping, delta, maxSpeed, undefined, eps); state.offset = loop ? misc.repeat(pos.current, 1) : misc.clamp(pos.current, 0, 1); if (path.getCurveLengths().length > 0) { path.getPointAt(state.offset, state.point); path.getTangentAt(state.offset, state.tangent).normalize(); path.getPointAt(misc.repeat(pos.current - (lastOffset - state.offset), 1), state.next); const target = (object == null ? void 0 : object.current) instanceof THREE.Object3D ? object.current : camera; target.position.copy(state.point); if (focus) { easing.dampLookAt(target, isObject3DRef(focus) ? focus.current.getWorldPosition(vec) : focus, focusDamping, delta, maxSpeed, undefined, eps); } } }); return /*#__PURE__*/React.createElement("group", _extends({ ref: ref }, props), /*#__PURE__*/React.createElement(MotionContext.Provider, { value: state }, children, debug && /*#__PURE__*/React.createElement(Debug, { color: debugColor }))); }); export { MotionPathControls, useMotion };