154 lines
5.8 KiB
JavaScript
154 lines
5.8 KiB
JavaScript
/* global AFRAME, THREE */
|
|
AFRAME.registerComponent('hand-menu-button', {
|
|
schema: {
|
|
src: {default: ''},
|
|
srcHover: {default: ''},
|
|
mixin: {default: ''}
|
|
},
|
|
|
|
init: function () {
|
|
this.onWatchButtonHovered = this.onWatchButtonHovered.bind(this);
|
|
this.onAnimationComplete = this.onAnimationComplete.bind(this);
|
|
this.onCollisionStarted = this.onCollisionStarted.bind(this);
|
|
this.onCollisionEnded = this.onCollisionEnded.bind(this);
|
|
this.onAnimationBegin = this.onAnimationBegin.bind(this);
|
|
this.onPinchEnded = this.onPinchEnded.bind(this);
|
|
|
|
this.el.addEventListener('obbcollisionstarted', this.onCollisionStarted);
|
|
this.el.addEventListener('obbcollisionended', this.onCollisionEnded);
|
|
this.el.object3D.renderOrder = 1000;
|
|
|
|
this.menuEl = this.el.parentEl;
|
|
this.handMenuEl = this.el.sceneEl.querySelector('[hand-menu]');
|
|
|
|
this.menuEl.addEventListener('animationbegin', this.onAnimationBegin);
|
|
this.menuEl.addEventListener('animationcomplete', this.onAnimationComplete);
|
|
},
|
|
|
|
onAnimationBegin: function (evt) {
|
|
// To prevent menu activations while animation is running.
|
|
if (evt.detail.name === 'animation__open') { this.menuOpen = false; }
|
|
},
|
|
|
|
onAnimationComplete: function (evt) {
|
|
if (evt.detail.name === 'animation__open') { this.menuOpen = true; }
|
|
if (evt.detail.name === 'animation__close') { this.menuOpen = false; }
|
|
},
|
|
|
|
onCollisionStarted: function (evt) {
|
|
var withEl = evt.detail.withEl;
|
|
if (this.handMenuEl === withEl ||
|
|
!withEl.components['hand-tracking-controls']) { return; }
|
|
if (!this.menuOpen) { return; }
|
|
this.handHoveringEl = withEl;
|
|
this.el.emit('watchbuttonhoverstarted');
|
|
},
|
|
|
|
onCollisionEnded: function (evt) {
|
|
var withEl = evt.detail.withEl;
|
|
if (this.handMenuEl === withEl ||
|
|
!withEl.components['hand-tracking-controls']) { return; }
|
|
this.disableHover();
|
|
this.handHoveringEl = undefined;
|
|
this.el.emit('watchbuttonhoverended');
|
|
},
|
|
|
|
enableHover: function () {
|
|
this.handHoveringEl.addEventListener('pinchended', this.onPinchEnded);
|
|
this.el.setAttribute('material', 'src', this.data.srcHover);
|
|
},
|
|
|
|
disableHover: function () {
|
|
if (!this.handHoveringEl) { return; }
|
|
this.handHoveringEl.removeEventListener('pinchended', this.onPinchEnded);
|
|
this.el.setAttribute('material', 'src', this.data.src);
|
|
},
|
|
|
|
onPinchEnded: (function () {
|
|
var spawnPosition = new THREE.Vector3(0, 1, 0);
|
|
return function () {
|
|
var cubeEl;
|
|
var newEntityEl;
|
|
if (!this.menuOpen) { return; }
|
|
this.menuOpen = false;
|
|
if (!this.handHoveringEl || !this.data.mixin) { return; }
|
|
// Spawn shape a little above the menu.
|
|
spawnPosition.set(0, 1, 0);
|
|
// Apply rotation of the menu.
|
|
spawnPosition.applyQuaternion(this.el.parentEl.object3D.quaternion);
|
|
// 20cm above the menu.
|
|
spawnPosition.normalize().multiplyScalar(0.2);
|
|
spawnPosition.add(this.el.parentEl.object3D.position);
|
|
|
|
newEntityEl = document.createElement('a-entity');
|
|
newEntityEl.setAttribute('mixin', this.data.mixin);
|
|
newEntityEl.setAttribute('position', spawnPosition);
|
|
this.el.sceneEl.appendChild(newEntityEl);
|
|
this.handHoveringEl.removeEventListener('pinchended', this.onPinchEnded);
|
|
};
|
|
})(),
|
|
|
|
onWatchButtonHovered: function (evt) {
|
|
if (evt.target === this.el || !this.handHoveringEl) { return; }
|
|
this.disableHover();
|
|
this.handHoveringEl = undefined;
|
|
}
|
|
});
|
|
|
|
/*
|
|
User's hand can collide with multiple buttons simultaneously but only want one in a hovered state.
|
|
This system keeps track of all the collided buttons, keeping just the closest to the hand in a hovered state.
|
|
*/
|
|
AFRAME.registerSystem('hand-menu-button', {
|
|
init: function () {
|
|
this.onWatchButtonHovered = this.onWatchButtonHovered.bind(this);
|
|
this.el.parentEl.addEventListener('watchbuttonhoverended', this.onWatchButtonHovered);
|
|
this.el.parentEl.addEventListener('watchbuttonhoverstarted', this.onWatchButtonHovered);
|
|
this.hoveredButtonEls = [];
|
|
},
|
|
|
|
tick: function () {
|
|
var buttonWorldPosition = new THREE.Vector3();
|
|
var thumbPosition;
|
|
var smallestDistance = 1000000;
|
|
var currentDistance;
|
|
var closestButtonEl;
|
|
if (this.hoveredButtonEls.length < 2) { return; }
|
|
thumbPosition = this.hoveredButtonEls[0].components['hand-menu-button'].handHoveringEl.components['obb-collider'].trackedObject3D.position;
|
|
for (var i = 0; i < this.hoveredButtonEls.length; ++i) {
|
|
this.hoveredButtonEls[i].object3D.getWorldPosition(buttonWorldPosition);
|
|
currentDistance = buttonWorldPosition.distanceTo(thumbPosition);
|
|
if (currentDistance < smallestDistance) {
|
|
closestButtonEl = this.hoveredButtonEls[i];
|
|
smallestDistance = currentDistance;
|
|
}
|
|
}
|
|
|
|
if (this.hoveredButtonEl === closestButtonEl) { return; }
|
|
|
|
this.hoveredButtonEl = closestButtonEl;
|
|
|
|
for (i = 0; i < this.hoveredButtonEls.length; ++i) {
|
|
if (!this.hoveredButtonEls[i].components['hand-menu-button'].handHoveringEl) { continue; }
|
|
if (this.hoveredButtonEls[i] === closestButtonEl) {
|
|
this.hoveredButtonEls[i].components['hand-menu-button'].enableHover();
|
|
continue;
|
|
}
|
|
this.hoveredButtonEls[i].components['hand-menu-button'].disableHover();
|
|
}
|
|
},
|
|
|
|
onWatchButtonHovered: function (evt) {
|
|
this.buttonEls = this.el.sceneEl.querySelectorAll('[hand-menu-button]');
|
|
this.hoveredButtonEls = [];
|
|
for (var i = 0; i < this.buttonEls.length; ++i) {
|
|
if (!this.buttonEls[i].components['hand-menu-button'].handHoveringEl) { continue; }
|
|
this.hoveredButtonEls.push(this.buttonEls[i]);
|
|
}
|
|
if (this.hoveredButtonEls.length === 1) {
|
|
this.hoveredButtonEl = this.hoveredButtonEls[0];
|
|
this.hoveredButtonEls[0].components['hand-menu-button'].enableHover();
|
|
}
|
|
}
|
|
});
|