diff --git a/CHANGELOG.md b/CHANGELOG.md index b88fbde..72733be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased][] +### Added + +- Support click and drag for children of entities and the camera + ## [1.3.3][] - 2016-11-05 ### Fixed diff --git a/examples/children/index.html b/examples/children/index.html new file mode 100644 index 0000000..401c0be --- /dev/null +++ b/examples/children/index.html @@ -0,0 +1,25 @@ + + + A-Frame Click & Drag Component - Basic + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/index.html b/examples/index.html index 97d23c4..cbee92c 100644 --- a/examples/index.html +++ b/examples/index.html @@ -35,6 +35,12 @@
+ Children Demo +

Child entities can be dragged around too!

+

In this example, the cube is a child of the sphere, which is a child of the camera.

+ +
+ Physics Demo

Calculating the velocity at the time of drag end.

Combined with a physics library (for example; aframe-extras physics), we get some very nice interactions.

diff --git a/src/index.js b/src/index.js index 397b759..a7e7f49 100644 --- a/src/index.js +++ b/src/index.js @@ -19,19 +19,31 @@ function forceWorldUpdate(threeElement) { element.updateMatrixWorld(true); } +function forEachParent(element, lambda) { + while (element.attachedToParent) { + element = element.parentElement; + lambda(element); + } +} + +function someParent(element, lambda) { + while (element.attachedToParent) { + element = element.parentElement; + if (lambda(element)) { + return true; + } + } +} + function cameraPositionToVec3(camera, vec3) { - let element = camera; - vec3.set( - element.components.position.data.x, - element.components.position.data.y, - element.components.position.data.z + camera.components.position.data.x, + camera.components.position.data.y, + camera.components.position.data.z ); - while (element.attachedToParent) { - - element = element.parentElement; + forEachParent(camera, element => { if (element.components && element.components.position) { vec3.set( @@ -41,7 +53,7 @@ function cameraPositionToVec3(camera, vec3) { ); } - } + }); } @@ -310,6 +322,10 @@ function dragItem(THREE, element, offset, camera, depth, mouseInfo, lockToLocalR z: THREE.Math.radToDeg(rotationEuler.z), }; + const activeCamera = element.sceneEl.systems.camera.activeCameraEl; + + const isChildOfActiveCamera = someParent(element, parent => parent === activeCamera); + function onMouseMove({clientX, clientY}) { lastMouseInfo = {clientX, clientY}; @@ -330,26 +346,49 @@ function dragItem(THREE, element, offset, camera, depth, mouseInfo, lockToLocalR if (lockToLocalRotation) { + let rotationDiff; + // Start by rotating backwards from the initial camera rotation - const rotationDiff = rotationQuaternion.copy(startCameraRotationInverse) - // Then add the current camera rotation - .multiply(threeCamera.getWorldQuaternion()); + rotationDiff = rotationQuaternion.copy(startCameraRotationInverse); // rotate the offset offsetVector.set(offset.x, offset.y, offset.z); + + // Then add the current camera rotation + rotationDiff = rotationQuaternion.multiply(threeCamera.getWorldQuaternion()); + offsetVector.applyQuaternion(rotationDiff); - // And correctly offset rotation - rotationDiff.multiply(startElementRotation); - rotationEuler.setFromQuaternion(rotationDiff, elementRotationOrder); + if (!isChildOfActiveCamera) { + // And correctly offset rotation + rotationDiff.multiply(startElementRotation); + + rotationEuler.setFromQuaternion(rotationDiff, elementRotationOrder); + } nextRotation.x = THREE.Math.radToDeg(rotationEuler.x); nextRotation.y = THREE.Math.radToDeg(rotationEuler.y); nextRotation.z = THREE.Math.radToDeg(rotationEuler.z); + } const nextPosition = {x: x - offsetVector.x, y: y - offsetVector.y, z: z - offsetVector.z}; + // When the element has parents, we need to convert its new world position + // into new local position of its parent element + if (element.parentEl !== element.sceneEl) { + + // The new world position + offsetVector.set(nextPosition.x, nextPosition.y, nextPosition.z); + + // Converted + element.parentEl.object3D.worldToLocal(offsetVector); + + nextPosition.x = offsetVector.x; + nextPosition.y = offsetVector.y; + nextPosition.z = offsetVector.z; + } + element.emit(DRAG_MOVE_EVENT, {nextPosition, nextRotation, clientX, clientY}); element.setAttribute('position', nextPosition);