Skip to content

Commit 8f7a65f

Browse files
committed
Add watch-style UI example
1 parent 86089c6 commit 8f7a65f

File tree

6 files changed

+582
-0
lines changed

6 files changed

+582
-0
lines changed

Diff for: examples/mixed-reality/watch/button.js

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* global AFRAME */
2+
AFRAME.registerComponent('button', {
3+
init: function () {
4+
var buttonContainerEl = this.buttonContainerEl = document.createElement('div');
5+
var buttonWristEl = this.buttonWristEl = document.createElement('button');
6+
var buttonPalmEl = this.buttonPalmEl = document.createElement('button');
7+
8+
var style = document.createElement('style');
9+
var css =
10+
'.a-button-container {box-sizing: border-box; display: inline-block; height: 34px; padding: 0;;' +
11+
'bottom: 20px; width: 200px; left: calc(50% - 75px); position: absolute; color: white;' +
12+
'font-size: 20px; line-height: 20px; border: none;' +
13+
'border-radius: 5px}' +
14+
'.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;}' +
15+
'.a-button:hover {border-color: #ef2d5e; color: #ef2d5e}' +
16+
'.a-button.selected {color: white; background-color: #ef2d5e; border-color: #ef2d5e}';
17+
18+
if (style.styleSheet) {
19+
style.styleSheet.cssText = css;
20+
} else {
21+
style.appendChild(document.createTextNode(css));
22+
}
23+
document.getElementsByTagName('head')[0].appendChild(style);
24+
25+
buttonContainerEl.classList.add('a-button-container');
26+
27+
buttonPalmEl.classList.add('a-button');
28+
buttonPalmEl.classList.add('selected');
29+
buttonPalmEl.addEventListener('click', this.onClick.bind(this));
30+
buttonContainerEl.appendChild(buttonPalmEl);
31+
32+
buttonWristEl.classList.add('a-button');
33+
buttonWristEl.addEventListener('click', this.onClick.bind(this));
34+
buttonContainerEl.appendChild(buttonWristEl);
35+
36+
this.el.sceneEl.appendChild(buttonContainerEl);
37+
buttonWristEl.innerHTML = 'WRIST';
38+
buttonPalmEl.innerHTML = 'PALM';
39+
},
40+
41+
onClick: function (evt) {
42+
if (evt.target === this.buttonPalmEl) {
43+
this.el.querySelector('[hand-menu]').setAttribute('hand-menu', 'location', 'palm');
44+
this.buttonPalmEl.classList.add('selected');
45+
this.buttonWristEl.classList.remove('selected');
46+
} else {
47+
this.el.querySelector('[hand-menu]').setAttribute('hand-menu', 'location', 'wrist');
48+
this.buttonPalmEl.classList.remove('selected');
49+
this.buttonWristEl.classList.add('selected');
50+
}
51+
}
52+
});

Diff for: examples/mixed-reality/watch/hand-menu-button.js

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/* global AFRAME, THREE */
2+
AFRAME.registerComponent('hand-menu-button', {
3+
schema: {
4+
src: {default: ''},
5+
srcHover: {default: ''},
6+
mixin: {default: ''}
7+
},
8+
9+
init: function () {
10+
this.onWatchButtonHovered = this.onWatchButtonHovered.bind(this);
11+
this.onAnimationComplete = this.onAnimationComplete.bind(this);
12+
this.onCollisionStarted = this.onCollisionStarted.bind(this);
13+
this.onCollisionEnded = this.onCollisionEnded.bind(this);
14+
this.onAnimationBegin = this.onAnimationBegin.bind(this);
15+
this.onPinchEnded = this.onPinchEnded.bind(this);
16+
17+
this.el.addEventListener('obbcollisionstarted', this.onCollisionStarted);
18+
this.el.addEventListener('obbcollisionended', this.onCollisionEnded);
19+
this.el.object3D.renderOrder = 1000;
20+
21+
this.menuEl = this.el.parentEl;
22+
this.handMenuEl = this.el.sceneEl.querySelector('[hand-menu]');
23+
24+
this.menuEl.addEventListener('animationbegin', this.onAnimationBegin);
25+
this.menuEl.addEventListener('animationcomplete', this.onAnimationComplete);
26+
},
27+
28+
onAnimationBegin: function (evt) {
29+
// To prevent menu activations while animation is running.
30+
if (evt.detail.name === 'animation__open') { this.menuOpen = false; }
31+
},
32+
33+
onAnimationComplete: function (evt) {
34+
if (evt.detail.name === 'animation__open') { this.menuOpen = true; }
35+
if (evt.detail.name === 'animation__close') { this.menuOpen = false; }
36+
},
37+
38+
onCollisionStarted: function (evt) {
39+
var withEl = evt.detail.withEl;
40+
if (this.handMenuEl === withEl ||
41+
!withEl.components['hand-tracking-controls']) { return; }
42+
if (!this.menuOpen) { return; }
43+
this.handHoveringEl = withEl;
44+
this.el.emit('watchbuttonhoverstarted');
45+
},
46+
47+
onCollisionEnded: function (evt) {
48+
var withEl = evt.detail.withEl;
49+
if (this.handMenuEl === withEl ||
50+
!withEl.components['hand-tracking-controls']) { return; }
51+
this.disableHover();
52+
this.handHoveringEl = undefined;
53+
this.el.emit('watchbuttonhoverended');
54+
},
55+
56+
enableHover: function () {
57+
this.handHoveringEl.addEventListener('pinchended', this.onPinchEnded);
58+
this.el.setAttribute('material', 'src', this.data.srcHover);
59+
},
60+
61+
disableHover: function () {
62+
if (!this.handHoveringEl) { return; }
63+
this.handHoveringEl.removeEventListener('pinchended', this.onPinchEnded);
64+
this.el.setAttribute('material', 'src', this.data.src);
65+
},
66+
67+
onPinchEnded: (function () {
68+
var spawnPosition = new THREE.Vector3(0, 1, 0);
69+
return function () {
70+
var cubeEl;
71+
var newEntityEl;
72+
if (!this.menuOpen) { return; }
73+
this.menuOpen = false;
74+
if (!this.handHoveringEl || !this.data.mixin) { return; }
75+
// Spawn shape a little above the menu.
76+
spawnPosition.set(0, 1, 0);
77+
// Apply rotation of the menu.
78+
spawnPosition.applyQuaternion(this.el.parentEl.object3D.quaternion);
79+
// 20cm above the menu.
80+
spawnPosition.normalize().multiplyScalar(0.2);
81+
spawnPosition.add(this.el.parentEl.object3D.position);
82+
83+
newEntityEl = document.createElement('a-entity');
84+
newEntityEl.setAttribute('mixin', this.data.mixin);
85+
newEntityEl.setAttribute('position', spawnPosition);
86+
this.el.sceneEl.appendChild(newEntityEl);
87+
this.handHoveringEl.removeEventListener('pinchended', this.onPinchEnded);
88+
};
89+
})(),
90+
91+
onWatchButtonHovered: function (evt) {
92+
if (evt.target === this.el || !this.handHoveringEl) { return; }
93+
this.disableHover();
94+
this.handHoveringEl = undefined;
95+
}
96+
});
97+
98+
/*
99+
User's hand can collide with multiple buttons simulatenously but only want one in a hovered state.
100+
This system keeps track of all the collided buttons, keeping just the closest to the hand in a hovered state.
101+
*/
102+
AFRAME.registerSystem('hand-menu-button', {
103+
init: function () {
104+
this.onWatchButtonHovered = this.onWatchButtonHovered.bind(this);
105+
this.el.parentEl.addEventListener('watchbuttonhoverended', this.onWatchButtonHovered);
106+
this.el.parentEl.addEventListener('watchbuttonhoverstarted', this.onWatchButtonHovered);
107+
this.hoveredButtonEls = [];
108+
},
109+
110+
tick: function () {
111+
var buttonWorldPosition = new THREE.Vector3();
112+
var thumbPosition;
113+
var smallestDistance = 1000000;
114+
var currentDistance;
115+
var closestButtonEl;
116+
if (this.hoveredButtonEls.length < 2) { return; }
117+
thumbPosition = this.hoveredButtonEls[0].components['hand-menu-button'].handHoveringEl.components['obb-collider'].trackedObject3D.position;
118+
for (var i = 0; i < this.hoveredButtonEls.length; ++i) {
119+
this.hoveredButtonEls[i].object3D.getWorldPosition(buttonWorldPosition);
120+
currentDistance = buttonWorldPosition.distanceTo(thumbPosition);
121+
if (currentDistance < smallestDistance) {
122+
closestButtonEl = this.hoveredButtonEls[i];
123+
smallestDistance = currentDistance;
124+
}
125+
}
126+
127+
if (this.hoveredButtonEl === closestButtonEl) { return; }
128+
129+
this.hoveredButtonEl = closestButtonEl;
130+
131+
for (i = 0; i < this.hoveredButtonEls.length; ++i) {
132+
if (!this.hoveredButtonEls[i].components['hand-menu-button'].handHoveringEl) { continue; }
133+
if (this.hoveredButtonEls[i] === closestButtonEl) {
134+
this.hoveredButtonEls[i].components['hand-menu-button'].enableHover();
135+
continue;
136+
}
137+
this.hoveredButtonEls[i].components['hand-menu-button'].disableHover();
138+
}
139+
},
140+
141+
onWatchButtonHovered: function (evt) {
142+
this.buttonEls = this.el.sceneEl.querySelectorAll('[hand-menu-button]');
143+
this.hoveredButtonEls = [];
144+
for (var i = 0; i < this.buttonEls.length; ++i) {
145+
if (!this.buttonEls[i].components['hand-menu-button'].handHoveringEl) { continue; }
146+
this.hoveredButtonEls.push(this.buttonEls[i]);
147+
}
148+
if (this.hoveredButtonEls.length === 1) {
149+
this.hoveredButtonEl = this.hoveredButtonEls[0];
150+
this.hoveredButtonEls[0].components['hand-menu-button'].enableHover();
151+
}
152+
}
153+
});

0 commit comments

Comments
 (0)