@react-three/drei
Version:
useful add-ons for react-three-fiber
128 lines (124 loc) • 4.88 kB
JavaScript
import _extends from '@babel/runtime/helpers/esm/extends';
import * as React from 'react';
import * as THREE from 'three';
import { useThree } from '@react-three/fiber';
import { useGesture } from '@use-gesture/react';
const initialModelPosition = /* @__PURE__ */new THREE.Vector3();
const mousePosition2D = /* @__PURE__ */new THREE.Vector2();
const mousePosition3D = /* @__PURE__ */new THREE.Vector3();
const dragOffset = /* @__PURE__ */new THREE.Vector3();
const dragPlaneNormal = /* @__PURE__ */new THREE.Vector3();
const dragPlane = /* @__PURE__ */new THREE.Plane();
const DragControls = /*#__PURE__*/React.forwardRef(({
autoTransform = true,
matrix,
axisLock,
dragLimits,
onHover,
onDragStart,
onDrag,
onDragEnd,
children,
dragConfig,
...props
}, fRef) => {
const defaultControls = useThree(state => state.controls);
const {
camera,
size,
raycaster,
invalidate
} = useThree();
const ref = React.useRef(null);
const bind = useGesture({
onHover: ({
hovering
}) => onHover && onHover(hovering !== null && hovering !== void 0 ? hovering : false),
onDragStart: ({
event
}) => {
if (defaultControls) defaultControls.enabled = false;
const {
point
} = event;
ref.current.matrix.decompose(initialModelPosition, new THREE.Quaternion(), new THREE.Vector3());
mousePosition3D.copy(point);
dragOffset.copy(mousePosition3D).sub(initialModelPosition);
onDragStart && onDragStart(initialModelPosition);
invalidate();
},
onDrag: ({
xy: [dragX, dragY],
intentional
}) => {
if (!intentional) return;
const normalizedMouseX = (dragX - size.left) / size.width * 2 - 1;
const normalizedMouseY = -((dragY - size.top) / size.height) * 2 + 1;
mousePosition2D.set(normalizedMouseX, normalizedMouseY);
raycaster.setFromCamera(mousePosition2D, camera);
if (!axisLock) {
camera.getWorldDirection(dragPlaneNormal).negate();
} else {
switch (axisLock) {
case 'x':
dragPlaneNormal.set(1, 0, 0);
break;
case 'y':
dragPlaneNormal.set(0, 1, 0);
break;
case 'z':
dragPlaneNormal.set(0, 0, 1);
break;
}
}
dragPlane.setFromNormalAndCoplanarPoint(dragPlaneNormal, mousePosition3D);
raycaster.ray.intersectPlane(dragPlane, mousePosition3D);
const previousLocalMatrix = ref.current.matrix.clone();
const previousWorldMatrix = ref.current.matrixWorld.clone();
const intendedNewPosition = new THREE.Vector3(mousePosition3D.x - dragOffset.x, mousePosition3D.y - dragOffset.y, mousePosition3D.z - dragOffset.z);
if (dragLimits) {
intendedNewPosition.x = dragLimits[0] ? Math.max(Math.min(intendedNewPosition.x, dragLimits[0][1]), dragLimits[0][0]) : intendedNewPosition.x;
intendedNewPosition.y = dragLimits[1] ? Math.max(Math.min(intendedNewPosition.y, dragLimits[1][1]), dragLimits[1][0]) : intendedNewPosition.y;
intendedNewPosition.z = dragLimits[2] ? Math.max(Math.min(intendedNewPosition.z, dragLimits[2][1]), dragLimits[2][0]) : intendedNewPosition.z;
}
if (autoTransform) {
ref.current.matrix.setPosition(intendedNewPosition);
const deltaLocalMatrix = ref.current.matrix.clone().multiply(previousLocalMatrix.invert());
const deltaWorldMatrix = ref.current.matrix.clone().multiply(previousWorldMatrix.invert());
onDrag && onDrag(ref.current.matrix, deltaLocalMatrix, ref.current.matrixWorld, deltaWorldMatrix);
} else {
const tempMatrix = new THREE.Matrix4().copy(ref.current.matrix);
tempMatrix.setPosition(intendedNewPosition);
const deltaLocalMatrix = tempMatrix.clone().multiply(previousLocalMatrix.invert());
const deltaWorldMatrix = tempMatrix.clone().multiply(previousWorldMatrix.invert());
onDrag && onDrag(tempMatrix, deltaLocalMatrix, ref.current.matrixWorld, deltaWorldMatrix);
}
invalidate();
},
onDragEnd: () => {
if (defaultControls) defaultControls.enabled = true;
onDragEnd && onDragEnd();
invalidate();
}
}, {
drag: {
filterTaps: true,
threshold: 1,
...(typeof dragConfig === 'object' ? dragConfig : {})
}
});
React.useImperativeHandle(fRef, () => ref.current, []);
React.useLayoutEffect(() => {
if (!matrix) return;
// If the matrix is a real matrix4 it means that the user wants to control the gizmo
// In that case it should just be set, as a bare prop update would merely copy it
ref.current.matrix = matrix;
}, [matrix]);
return /*#__PURE__*/React.createElement("group", _extends({
ref: ref
}, bind(), {
matrix: matrix,
matrixAutoUpdate: false
}, props), children);
});
export { DragControls };