I have an annoying problem where my raycasting is fine with the mouse but not with touch.
I setup events as such
$(document).bind('mousedown', onDocumentMouseDown);
$(document).bind('mousewheel', onDocumentMouseWheel);
$(document).bind('touchstart', onDocumentTouchStart);
$(document).bind('touchmove', onDocumentTouchMove);
My mouse event
function onDocumentMouseDown(e) {
e.preventDefault();
detectHotspotClick(e.pageX,e.pageY);
}
My Touch event
function onDocumentTouchStart(e) {
e.preventDefault();
var event = e.originalEvent;
if (event.touches.length == 1) {
var t=event.touches[0];
draggingY = t.pageY;
detectHotspotClick(t.pageX,t.pageY);
}
if (event.touches.length == 2) {
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
}
}
My detection
function detectHotspotClick(x,y){
console.log('detectHotspotClick('+ x + "," + y+ ")" );
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
mouse.x = ( x / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( y / renderer.domElement.height ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( targetList, true );
if ( intersects.length > 0)
hotspotClick(intersects[ 0 ].object);
}
This works fine with mouse, but I notice on Touch, the mouse.x value always seems to be negetative and I'm not sure that's right.
Is there any other way of doing this I should consider?
Not sure why.. but it works with detectHotspotClick(t.pageX*2,t.pageY*2) on the touchevent, which must report different figures to the mouse event.
Related
I have a weird problem. I'm creating a test game.Which uses touch events and canvas.There is a ball that you can push it and when you release your finger it must go(something like angry birds).
I made that in mouse and it work correctly.I wanna make it on android. But it doesn't run.But when you use 2 finger it works correctly.
I'm sure the problem is here :
canvas.addEventListener("touchstart",function(event){
event.preventDefault();
if (event.touches[0].clientX >= x && event.touches[0].clientX <= x + (radius * 2) && event.touches[0].clientY >= y && event.touches[0].clientY <= y + (radius * 2)) {
dx = 0;
dy = 0;
isindrag = true;
oldx = x;
oldy = y;
}
});
canvas.addEventListener("touchmove", function (event) {
event.preventDefault();
if (isindrag) {
x = event.touches[0].clientX;
y = event.touches[0].clientY;
}
});
canvas.addEventListener("touchend", function (event) {
var touchX = event.touches[0].clientX;
var touchY = event.touches[0].clientY;
if (isindrag && touchX < canvas.width && touchY < canvas.height && touchX > 0 && touchY > 0) {
isindrag = false;
dx = -(x - oldx) / 30;
dy = -(y - oldy) / 30;
ismoving = true;
}
});
canvas.addEventListener("touchcancel", function(event){
event.preventDefault();
});
x : x of ball
y : y of ball
dx : Delta x of ball
dy : delta y of ball
radius : radius of ball
Can you help me?
The touches object of a touch event contains a collection of points currently on the screen. Now imagine that you put a single finger onto the surface and release it. This will trigger a touchend event but as the touches object just contains information on the active 'fingers', it will come up empty thus there is no clientX or clientY property to query. In a touchend event handler you need to use changedTouches instead of touches.
So try changing
var touchX = event.touches[0].clientX;
var touchY = event.touches[0].clientY;
to
var touchX = event.changedTouches[0].clientX;
var touchY = event.changedTouches[0].clientY;
I've been having issues with three.js raycasting recently.
I have a div element for which the element is not the full screen and it should not be the full screen and I think this is causing issues with the positioning of the raycast which I don't know how to fix.
let mouseVector = new THREE.Vector3()
let raycaster = new THREE.Raycaster()
Object.assign(mouseVector, {
x: 2 * (e.clientX / canvas.width) - 1,
y: 1 - 2 * (e.clientY / canvas.height)
})
raycaster.setFromCamera(mouseVector, camera)
let intersects = raycaster.intersectObject(scene.children)
for(const intersection of intersects) {
console.log(intersection)
}
mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObjects( scene.children, true );
console.log(intersects);
for(const ray of intersects) {
if(ray.object.name !== "Grid") {
if(smallestDist == undefined){
smallestDist1 = ray;
}else if(smallestDist > ray.distance){
smallestDist1 = ray
}
}
}
mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children );
for ( var i = 0; i < intersects.length; i++ ) {
if(intersects[i].object.name == "Grid"){
}else{
if(smallestDist == undefined){
smallestDist = intersects[i];
}else if(smallestDist > intersects[i].distance){
smallestDist = intersects[i]
}
}
}
console.log(smallestDist1)
if(smallestDist1){
const geometry = new THREE.SphereGeometry( 0.5, 15, 15 );
const material = new THREE.MeshBasicMaterial( {color: 0xFF0000} );
const sphere = new THREE.Mesh( geometry, material );
sphere.position.set(smallestDist1.point.x, smallestDist1.point.y, smallestDist1.point.z)
sphere.name = "TestSphere"
scene.add( sphere );
}
Above is the code i've tried however as the element with the 3D object isn't full window sized I think it's bugging out. Any help is appreciated.
Try to compute mouse like so:
const rect = renderer.domElement.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
mouse.x = ( x / canvas.clientWidth ) * 2 - 1;
mouse.y = ( y / canvas.clientHeight) * - 2 + 1
The top-left point of the browser window is the origin of coordinate (0, 0) for MouseEvent, while the exact center point of canvas for threejs.
Click here to see coordinate details.
As shown the image above, four corner points of the canvas is equivalent to (-1, 1), (1, 1), (1, -1), (-1, -1) under threejs context.
Otherwise, the canvas might have offsets to the browser (0, 0), which can be accessed with canvas.offsetTop and cavas.offsetLeft.
With all above considered, any MouseEvent client coordinate (event.clientX, event.clientY) should be mapped to threejs coordinate as below:
mouse.x = ( (event.clientX - canvas.offsetLeft) / canvas.clientWidth ) * 2 - 1;
mouse.y = ( (event.clientY - canvas.offsetTop) / canvas.clientHeight ) * -2 + 1;
I want to detect a click event on a three.js sprite here's the code:
function bindEvents(state) {
let intersected;
function onDocumentMouseDown(event) {
event.preventDefault();
const mouseX = (event.clientX / window.innerWidth) * 2 - 1;
const mouseY = - (event.clientY / window.innerHeight) * 2 + 1;
const camera = state.threeD.elements.camera;
const raycaster = state.threeD.raycaster;
raycaster.setFromCamera(new THREE.Vector2(mouseX, mouseY), camera);
var intersects = raycaster.intersectObjects(state.clickables?state.clickables:[]);
if (intersects.length > 0) {
if (intersected) intersected.material.map = intersected.currentMap;
intersected = intersects[0].object;
intersected.currentMap = intersected.material.map;
intersected.material.map = selectCityTexture(128, '#cccccc', 'cccccc');
}
}
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('touchstart', onDocumentMouseDown, false);
}
This almost works, the mouse position is offset by some amount. Probably some calculation error, I have no idea how the above code works please help.
const mouseX = (event.clientX / window.innerWidth) * 2 - 1;
const mouseY = - (event.clientY / window.innerHeight) * 2 + 1;
these are changed with
const mouseX = (event.clientX / canvas.width) * 2 - 1;
const mouseY = - (event.clientY / canvas.height) * 2 + 1;
I want to add Pinch Zoom to my three.js panorama player.
I've had a look around and it seems TrackBallControls.js might have this build in?
I've tried implementing it, however I'm getting a
'Uncaught TypeError: undefined is not a function'
on
var controls = new THREE.TrackballControls( camera );
Now because of the system I'm having to load all Three.js scripts dynamically using Sid.js, would this be a reason why it can't seem to find THREE.TrackballControls?
Is this even the right solution for pinch and zoom on mobile in Three?
TrackBallControls.js does have touch zoom built in, relevant code below; however the files is not a part of the Three.js library. It only exists in the example projects. You can find the source code here.
function touchstart( event ) {
if ( _this.enabled === false ) return;
switch ( event.touches.length ) {
case 1:
_state = STATE.TOUCH_ROTATE;
_rotateStart.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_rotateEnd.copy( _rotateStart );
break;
case 2:
_state = STATE.TOUCH_ZOOM_PAN;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
_panStart.copy( getMouseOnScreen( x, y ) );
_panEnd.copy( _panStart );
break;
default:
_state = STATE.NONE;
}
_this.dispatchEvent( startEvent );
}
function touchmove( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.touches.length ) {
case 1:
_rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
break;
case 2:
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
_panEnd.copy( getMouseOnScreen( x, y ) );
break;
default:
_state = STATE.NONE;
}
}
function touchend( event ) {
if ( _this.enabled === false ) return;
switch ( event.touches.length ) {
case 1:
_rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_rotateStart.copy( _rotateEnd );
break;
case 2:
_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
_panEnd.copy( getMouseOnScreen( x, y ) );
_panStart.copy( _panEnd );
break;
}
_state = STATE.NONE;
_this.dispatchEvent( endEvent );
}
If anyone else needs the code I modified it a bit
function onDocumentTouchStart(event) {
if (event.touches.length == 1) {
console.log('1');
event.preventDefault();
onPointerDownPointerX = event.touches[0].pageX;
onPointerDownPointerY = event.touches[0].pageY;
onPointerDownLon = lon;
onPointerDownLat = lat;
detectHotspotClick();
}
if (event.touches.length == 2) {
console.log('2');
_state = STATE.TOUCH_ZOOM_PAN;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
}
}
function onDocumentTouchMove(event) {
if (event.touches.length == 1) {
event.preventDefault();
lon = (onPointerDownPointerX - event.touches[0].pageX) * 0.1 + onPointerDownLon;
lat = (event.touches[0].pageY - onPointerDownPointerY) * 0.1 + onPointerDownLat;
}
if (event.touches.length == 2) {
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
_touchZoomDistanceStart = _touchZoomDistanceEnd;
setZoom(camera.fov * factor);
}
}
function onDocumentTouchEnd( event ) {
_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
}
function setZoom(fov){
camera.fov = fov;
if(camera.fov < 30) camera.fov = 30;
if(camera.fov > 100) camera.fov = 100;
camera.updateProjectionMatrix();
}
I'm experimenting with WebGL and Three.js,
But how to calculate the Z-Axis coordination while moving mouse?
I use this for X and Y:
function getCoord(e) {
X = e.layerX - canvas.offsetLeft;
Y = e.layerY - canvas.offsetTop;
}
how to get Z coordination?
Edit: I find this example, but can't figure it out myself...
https://github.com/mrdoob/three.js/blob/master/examples/webgl_interactive_voxelpainter.html
Thanks
Here is the answer...
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse2D.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse2D.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function render() {
mouse3D = projector.unprojectVector( mouse2D.clone(), camera );
...