Skip to content

Commit

Permalink
Add watch-style UI example
Browse files Browse the repository at this point in the history
  • Loading branch information
dmarcos committed Dec 7, 2023
1 parent 86089c6 commit 8f7a65f
Show file tree
Hide file tree
Showing 6 changed files with 582 additions and 0 deletions.
52 changes: 52 additions & 0 deletions examples/mixed-reality/watch/button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* global AFRAME */
AFRAME.registerComponent('button', {
init: function () {
var buttonContainerEl = this.buttonContainerEl = document.createElement('div');
var buttonWristEl = this.buttonWristEl = document.createElement('button');
var buttonPalmEl = this.buttonPalmEl = document.createElement('button');

var style = document.createElement('style');
var css =
'.a-button-container {box-sizing: border-box; display: inline-block; height: 34px; padding: 0;;' +
'bottom: 20px; width: 200px; left: calc(50% - 75px); position: absolute; color: white;' +
'font-size: 20px; line-height: 20px; border: none;' +
'border-radius: 5px}' +
'.a-button {cursor: pointer; padding: 0px 10px 0 10px; font-weight: bold; color: #666; border: 3px solid #666; box-sizing: border-box; vertical-align: middle; max-width: 200px; border-radius: 10px; height: 40px; background-color: white; margin: 0; margin-right: 10px;}' +
'.a-button:hover {border-color: #ef2d5e; color: #ef2d5e}' +
'.a-button.selected {color: white; background-color: #ef2d5e; border-color: #ef2d5e}';

if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
document.getElementsByTagName('head')[0].appendChild(style);

buttonContainerEl.classList.add('a-button-container');

buttonPalmEl.classList.add('a-button');
buttonPalmEl.classList.add('selected');
buttonPalmEl.addEventListener('click', this.onClick.bind(this));
buttonContainerEl.appendChild(buttonPalmEl);

buttonWristEl.classList.add('a-button');
buttonWristEl.addEventListener('click', this.onClick.bind(this));
buttonContainerEl.appendChild(buttonWristEl);

this.el.sceneEl.appendChild(buttonContainerEl);
buttonWristEl.innerHTML = 'WRIST';
buttonPalmEl.innerHTML = 'PALM';
},

onClick: function (evt) {
if (evt.target === this.buttonPalmEl) {
this.el.querySelector('[hand-menu]').setAttribute('hand-menu', 'location', 'palm');
this.buttonPalmEl.classList.add('selected');
this.buttonWristEl.classList.remove('selected');
} else {
this.el.querySelector('[hand-menu]').setAttribute('hand-menu', 'location', 'wrist');
this.buttonPalmEl.classList.remove('selected');
this.buttonWristEl.classList.add('selected');
}
}
});
153 changes: 153 additions & 0 deletions examples/mixed-reality/watch/hand-menu-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/* 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 simulatenously 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();
}
}
});
Loading

0 comments on commit 8f7a65f

Please sign in to comment.