xrsh-com/com/pressable.js

85 lines
2.9 KiB
JavaScript
Raw Normal View History

2024-03-07 17:34:49 +01:00
// this makes WebXR hand controls able to click things (by touching it)
AFRAME.registerComponent('pressable', {
schema: {
pressDistance: { default: 0.008 },
2025-02-28 17:05:43 +01:00
pressDuration: { default: 300 },
immersiveOnly: { default: true }
2024-03-07 17:34:49 +01:00
},
init: function() {
this.worldPosition = new THREE.Vector3();
this.raycaster = new THREE.Raycaster()
this.handEls = document.querySelectorAll('[hand-tracking-controls]');
this.pressed = false;
this.distance = -1
// we throttle by distance, to support scenes with loads of clickable objects (far away)
this.tick = this.throttleByDistance( () => this.detectPress() )
2025-02-28 17:05:43 +01:00
this.el.addEventListener("raycaster-intersected", (e) => {
if( !this.data || (this.data.immersiveOnly && !this.el.sceneEl.renderer.xr.isPresenting) ) return
this.el.emit('click', e.detail )
})
2024-03-07 17:34:49 +01:00
},
throttleByDistance: function(f){
return function(){
if( this.distance < 0 ) return f() // first call
if( !f.tid ){
let x = this.distance
let y = x*(x*0.05)*1000 // parabolic curve
f.tid = setTimeout( function(){
f.tid = null
f()
}, y )
}
}
},
detectPress: function(){
if( !this.el.sceneEl.renderer.xr.isPresenting ) return // ignore events in desktop mode
2024-03-07 17:34:49 +01:00
if( this.handEls.length == 0 ){
this.handEls = document.querySelectorAll('[hand-tracking-controls]');
}
var handEls = this.handEls;
var handEl;
let minDistance = 5
2024-03-07 17:34:49 +01:00
// compensate for an object inside a group
let object3D = this.el.object3D.type == "Group" ? this.el.object3D.children[0] : this.el.object3D
if( !object3D ) return
2024-03-07 17:34:49 +01:00
for (var i = 0; i < handEls.length; i++) {
handEl = handEls[i];
let indexTip = handEl.components['hand-tracking-controls'] ?
handEl.components['hand-tracking-controls'].indexTipPosition :
false
if( ! indexTip ) return // nothing to do here
2025-02-28 17:05:43 +01:00
this.raycaster.far = this.data.pressDistance
2024-03-07 17:34:49 +01:00
// Create a direction vector to negative Z
const direction = new THREE.Vector3(0,0,-1.0);
direction.normalize()
this.raycaster.set(indexTip, direction)
intersects = this.raycaster.intersectObjects([object3D],true)
2024-03-07 17:34:49 +01:00
object3D.getWorldPosition(this.worldPosition)
distance = indexTip.distanceTo(this.worldPosition)
minDistance = distance < minDistance ? distance : minDistance
if (intersects.length ){
this.i = this.i || 0;
if( !this.pressed ){
this.el.emit('pressedstarted', intersects);
this.el.emit('click', {intersection: intersects[0]});
this.pressed = setTimeout( () => {
this.el.emit('pressedended', intersects);
this.pressed = null
}, this.data.pressDuration )
2024-03-07 17:34:49 +01:00
}
}
}
this.distance = minDistance
},
2025-02-28 17:05:43 +01:00
2024-03-07 17:34:49 +01:00
});