xrsh-com/com/pressable.js
Leon van Kammen 987828b233
All checks were successful
/ mirror_to_github (push) Successful in 28s
/ test (push) Successful in 6s
launcher/handmenu demo
2025-03-14 14:49:04 +01:00

81 lines
2.9 KiB
JavaScript

// this makes WebXR hand controls able to click things (by touching it)
AFRAME.registerComponent('pressable', {
schema: {
pressDistance: { default: 0.005 },
pressDuration: { default: 300 },
immersiveOnly: { default: true }
},
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() )
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 )
})
},
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.handEls.length == 0 ){
this.handEls = document.querySelectorAll('[hand-tracking-controls]');
}
var handEls = this.handEls;
var handEl;
let minDistance = 5
// 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
for (var i = 0; i < handEls.length; i++) {
handEl = handEls[i];
let indexTip = handEl.object3D.getObjectByName('index-finger-tip')
if( ! indexTip ) return // nothing to do here
this.raycaster.far = this.data.pressDistance
// Create a direction vector to negative Z
const direction = new THREE.Vector3(0,0,-1.0);
direction.normalize()
this.raycaster.set(indexTip.position, direction)
intersects = this.raycaster.intersectObjects([object3D],true)
object3D.getWorldPosition(this.worldPosition)
distance = indexTip.position.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', intersects);
this.pressed = setTimeout( () => {
this.el.emit('pressedended', intersects);
this.pressed = null
}, this.data.pressDuration )
}
}
}
this.distance = minDistance
},
});