Support dragging child entities

This commit is contained in:
Jess Telford 2016-11-05 15:52:46 +11:00
parent 73c1d75f00
commit 877f0f12f1
4 changed files with 89 additions and 15 deletions

View file

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased][] ## [Unreleased][]
### Added
- Support click and drag for children of entities and the camera
## [1.3.3][] - 2016-11-05 ## [1.3.3][] - 2016-11-05
### Fixed ### Fixed

View file

@ -0,0 +1,25 @@
<html>
<head>
<title>A-Frame Click & Drag Component - Basic</title>
<script src="../build.js"></script>
</head>
<body>
<a-scene>
<a-cylinder click-drag position="1 0.75 1" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
<a-plane rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
<a-sky color="#ECECEC"></a-sky>
<a-entity position="0 0 3.8">
<a-camera look-controls-enabled="false" keyboard-controls="mode: fps">
<a-sphere click-drag position="0 0.5 -4" radius="1.25" color="#EF2D5E">
<a-box click-drag position="-1 0.5 1" rotation="0 45 0" width="1" height="1" depth="1" color="#4CC3D9"></a-box>
</a-sphere>
</a-camera>
</a-entity>
</a-scene>
</body>
</html>

View file

@ -35,6 +35,12 @@
<hr /> <hr />
<a class="demo-link" href="children/">Children Demo</a>
<p>Child entities can be dragged around too!</p>
<p>In this example, the cube is a child of the sphere, which is a child of the camera.</p>
<hr />
<a class="demo-link" href="physics/">Physics Demo</a> <a class="demo-link" href="physics/">Physics Demo</a>
<p>Calculating the velocity at the time of drag end.</p> <p>Calculating the velocity at the time of drag end.</p>
<p>Combined with a physics library (for example; <a href="https://github.com/donmccurdy/aframe-extras/tree/master/src/physics">aframe-extras physics</a>), we get some very nice interactions.</p> <p>Combined with a physics library (for example; <a href="https://github.com/donmccurdy/aframe-extras/tree/master/src/physics">aframe-extras physics</a>), we get some very nice interactions.</p>

View file

@ -19,19 +19,31 @@ function forceWorldUpdate(threeElement) {
element.updateMatrixWorld(true); 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) { function cameraPositionToVec3(camera, vec3) {
let element = camera;
vec3.set( vec3.set(
element.components.position.data.x, camera.components.position.data.x,
element.components.position.data.y, camera.components.position.data.y,
element.components.position.data.z camera.components.position.data.z
); );
while (element.attachedToParent) { forEachParent(camera, element => {
element = element.parentElement;
if (element.components && element.components.position) { if (element.components && element.components.position) {
vec3.set( 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), z: THREE.Math.radToDeg(rotationEuler.z),
}; };
const activeCamera = element.sceneEl.systems.camera.activeCameraEl;
const isChildOfActiveCamera = someParent(element, parent => parent === activeCamera);
function onMouseMove({clientX, clientY}) { function onMouseMove({clientX, clientY}) {
lastMouseInfo = {clientX, clientY}; lastMouseInfo = {clientX, clientY};
@ -330,26 +346,49 @@ function dragItem(THREE, element, offset, camera, depth, mouseInfo, lockToLocalR
if (lockToLocalRotation) { if (lockToLocalRotation) {
let rotationDiff;
// Start by rotating backwards from the initial camera rotation // Start by rotating backwards from the initial camera rotation
const rotationDiff = rotationQuaternion.copy(startCameraRotationInverse) rotationDiff = rotationQuaternion.copy(startCameraRotationInverse);
// Then add the current camera rotation
.multiply(threeCamera.getWorldQuaternion());
// rotate the offset // rotate the offset
offsetVector.set(offset.x, offset.y, offset.z); offsetVector.set(offset.x, offset.y, offset.z);
// Then add the current camera rotation
rotationDiff = rotationQuaternion.multiply(threeCamera.getWorldQuaternion());
offsetVector.applyQuaternion(rotationDiff); offsetVector.applyQuaternion(rotationDiff);
// And correctly offset rotation if (!isChildOfActiveCamera) {
rotationDiff.multiply(startElementRotation); // And correctly offset rotation
rotationEuler.setFromQuaternion(rotationDiff, elementRotationOrder); rotationDiff.multiply(startElementRotation);
rotationEuler.setFromQuaternion(rotationDiff, elementRotationOrder);
}
nextRotation.x = THREE.Math.radToDeg(rotationEuler.x); nextRotation.x = THREE.Math.radToDeg(rotationEuler.x);
nextRotation.y = THREE.Math.radToDeg(rotationEuler.y); nextRotation.y = THREE.Math.radToDeg(rotationEuler.y);
nextRotation.z = THREE.Math.radToDeg(rotationEuler.z); nextRotation.z = THREE.Math.radToDeg(rotationEuler.z);
} }
const nextPosition = {x: x - offsetVector.x, y: y - offsetVector.y, z: z - offsetVector.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.emit(DRAG_MOVE_EVENT, {nextPosition, nextRotation, clientX, clientY});
element.setAttribute('position', nextPosition); element.setAttribute('position', nextPosition);