I need to track mouse position relative to a <canvas> element in my app. Currently, I have a mousemove event listener attached to the <canvas> that updates my mouse position whenever it fires, using offsetX/offsetY when available, or layerX/layerY when the offsetX/Y is not available. Using offsetX/Y or layerX/Y gives me mouse coordinates relative to my <canvas>, which is exactly what I want. As my app works its magic, various CSS 3d transformations get applied to the <canvas>, and even when <canvas> is very transformed, offsetX/Y still gives me accurate coordinates within the <canvas>'s local, transformed coordinate-space.
That's kind of confusing, so I'll try stating an example. If my <canvas> is 100px in both width and height, and is located at (0,0) relative to the browser viewport, and I click at (50,50) (in viewport coords), that corresponds to (50,50) in my <canvas>, and 50 is the value that is (correctly) returned via offsetX and offsetY. If I then apply transform: translate3d(20px,20px,0px) to my <canvas> and click at (50,50) (in viewport coords), since my canvas has been shifted 20px down and 20px to the right, that actually corresponds to (30,30) relative to the <canvas>, and 30 is the value that is (correctly) returned via offsetX and offsetY.
The problem I'm facing is what to do when the user is not physically moving the mouse, yet the <canvas> is being transformed. I'm only updating the position of the mouse on mousemove events, so what do I do when there is no mousemove?
For example. My mouse is positioned at (50,50) and no transformations are applied to the <canvas>. My this.mouseX and this.mouseY are both equal to 50; they were saved at the final mousemove event when I moved the mouse to (50,50). Without moving the mouse at all, I apply the above transformation (transform: translate3d(20px,20px,0px)) to my <canvas>. Now, I need this.mouseX and this.mouseY to each be equal to 30, as that is my mouse's new position relative to the current transformation of <canvas>. But this.mouseX and this.mouseY are still equal to 50. Since I never moved the mouse, there was no mousemove event fired, and those saved coords never got updated.
How can I deal with this? I thought about creating a new jQuery event, manually assigning some properties (pageX and pageY?) based on my old/previous mouse position, and then triggering that event, but I don't think that's going to cause the browser to recalculate the offsetX and offsetY properties. I've also been thinking about taking the known old/previous mouse position and multiplying it by my transformation matrix, but that's going to get real complicated since my mouse coordinates are in 2d-space, but the transformations I'm applying to <canvas> are all 3d transformations.
I guess really, what I want to do is take my known 2d page position and raycast it into the 3d space and find out where I'm hitting the transformed <canvas>, all in javascript (jQuery is available).
Is this possible? Does this even make sense?
Works in all browsers
var mouseX=0;
var mouseY=0;
var canvas = document.querySelector('#canvas');
var rect = canvas.getBoundingClientRect();
document.onmousemove = function(e) {
mouseX=e.clientX-rect.left;
mouseY=e.clientY-rect.top;
};
function updateCoords() {
mouseX=e.clientX-mouseX;
mouseY=e.clientY-mouseY;
setTimeout(updatecoords,10);
}
Now we can call updateCoords() function once to repeatedly check for new position.
updateCoords();
You can add your code inside the updateCoords() function and it will be executed each 10 milliseconds
Concept: mouseX and mouseY variables get updated on mousemove event, and also get updated when there is any change in the canvas position.
It looks like you want to refresh your mouseposition-values even if you don't move your mouse. You should try something like this:
var event = '';
var counter = 1;
$(function(e){
event = e;
window.setInterval(refresh, 10);
});
$(document).mousemove(function(e){
event = e;
refresh;
});
function refresh(){
counter++;
$('#mousepos').val("event.pageX: " + event.pageX + ", event.pageY: " + event.pageY + ", counter: " + counter)
}
The counter is just for visualisation of the refresh. You can set the interval to everything you want (10 = 10ms = 0.01s) Just move everything from your .mousemove() event into this refresh() function and call it properly and your mouse position should update even if you don't move your mouse.
Look at this fiddle for a life example: http://jsfiddle.net/82cmxw8L/1
EDIT:
Because my fiddle didn't work for the asker, i updated it: http://jsfiddle.net/82cmxw8L/8/
New is, that the mouseposition is now set every 0.1 Second, no matter what, rather than being updated only when the mouse moves.
Related
How can I calculate offsetX and offsetY of the mouse cursor, relative to an element being transformed, even though the mouse is not moving?
https://jsfiddle.net/kevin_dorion/usrLd1x3/34/
// this is the value I need to update, even when the mouse is not moving
offsetEl.innerHTML = `x: ${evt.offsetX} y: ${evt.offsetY}`;
As you can see, on mousemove, I get the offsetX and offsetY values, which are exactly what I need. But I also need to be aware of these values changing when the mouse is not moving.
requestAnimationFrame(animate);
// how do I manually calculate offsetX and offsetY of the mouse cursor relative to the "el" element when the mouse is not moving?
I was thinking of keeping the last mouse event, use the pageX/pageY to position an element, then calculate its offset, but I can't get it working properly.
Thank you!
EDIT: I was thinking of storing the last known matrix / mouse coordinates combinaison, use the current matrix and make some interpolation, but I can't get it right either...
i am trying to use an example from the three.js page's, the voxel painter one. In this example the mouse coordinates's are used to move a roll-over helper that indicates the position where a box will be placed after the click.
mouse.set((event.clientX/window.innerWidth)*2-1, -(event.clientY/window.innerHeight)*2+1);
This piece of code calculates the position of the mouse all over the page.
I have added other div elements in the page such that the total amount of space for the webGL canvas is different from the total amount of space in the page, the new dimensions of the webGL canvas are 95% of the total height and 85% of the total width.
Now, the mouse position's over my webGL canavas is obviously different therefore the roll-over helper does not overlap anymore the position of the mouse. How have i to modify the above piece of code?
You could use jQuery to detect mouse position which works off any jQuery object passed to it.
$(function() {
var xCoord, yCoord;
$(document).on("mousemove", function(event) {
xCoord = event.pageX;
yCoord = event.pageY;
console.log("x: "+ xCoord+ ", y: "+ yCoord);
} );
} );
Just change the jQuery selector to your canvas and jQuery will handle the rest.
Hope this helps!
If you want the global position of the mouse cursor on your page, you should rather use the page coordinates like this:
event.pageX
event.pageY
I'm working on making a mobile web view for zooming and panning an image; using CSS transformations (scale) for zooming and jQuery's "offset()" function for panning.
The problem I am encountering is that if I zoom the image in or out before doing any panning, the image jumps to the top left or bottom right corner (respectively) of the view. After the initial jump, the panning and zooming return to working normally. No issues happen if I pan before zooming.
I've narrowed the issue down pretty close, as far as I can tell, to when the offset of the image is being changed during the panning gesture (touchmove). The event listener I am using is similar to:
function touchmoveListener(event) {
// get current touch event coordinates
var curX = event.X;
var curY = event.Y;
// pan image by changing offset
var curOffset = $("#image").offset();
var newOffset = {
left: curOffset.left + (curX - lastX),
top: curOffset.top + (curY - lastY)
};
$("#image").offset(newOffset);
// store coordinates for future panning
lastX = curtX;
lastY = curY;
}
I've console.log'ed the image's original offset, the new computed offset (before assigning), and finally the image's new offset after assignment. When I get the zoom-then-pan issue (zoom in to 2.5x, pan to the left), I get output like this:
original offset: {left: -215, top: -100} // normal original location, okay!
computed offset: {left: -216, top: -100} // pan 1px to the left, looks good!
assigned offset: {left: -441, top: -325} // what the...? both off by 225px!
I'm really not sure about what the problem is, since I am logging the coordinates of the exact object being passed into the offset() function, and yet the coordinates that are apparently being assigned are completely different. Does anyone have an idea of what's going wrong here? Any input would be appreciated.
Notes:
I've looked into the jQuery offset() after CSS transformation bug (http://bugs.jquery.com/ticket/8362) and I am testing using WebKit, but the issue I'm having is apparently with setting the offset, not accessing it.
lastX and lastY are initialized in response to the touchstart event (before touchmove triggers), so it's not a problem with them
Thanks,
-Brendan
.offset(coordinates) changes the position of the target element relative to the document.
If #image's initial position is something other than relative, that may explain the jump in positioning you see when the event listener is first initialized - the image's position is being changed relative to the document, instead of its initial position.
So I can make an element move with the mouse, however the problem is the offset is calculated from the top left corner of the element and doesn't take into account the mouse's position within the element causing the object to leap towards the mouse and preventing you from dragging it upwards or left.
So I came up with this function:
handle.on("mousemove", function(e) {
if (state.dragging) {
var paneOffset = pane.offset();
var mouseOffset = {
'top': e.pageY - paneOffset.top,
'left': e.pageX - paneOffset.left
}
pane.offset({
top: e.pageY - mouseOffset.top,
left: e.pageX - mouseOffset.left
});
}
});
Example: http://jsfiddle.net/SEKxc/
The problem with this is it stops the element from moving as it always calculates the element's same exact offset. How would I make the element follow the mouse but relative to the mouse's position with the object.
I am aware of jQueryUI having this functionality built-in but don't want to use that.
You could calculate the initial mouseoffset on dragstart and hence use it as a global variable from within the mousemove handler. Since the dragstart starts before mouse move i dont see a problem.
Update: you need to store both the objects initial offset and the mouses initial offset and calculate the relative offset of the mouse compared to the original on mouseMove then apply the same transformation to the element.
Store the distance between the mouse position and the element top left corner when you start dragging. Then you just subtract those from the mouse position to calculate the element position when the mouse moves.
I get mouse coordinates on some web page and save them.
$("div#container").mousemove( function(e) {
client_x = e.pageX;
client_y = e.pageY;
// save x,y
});
Now other person load that same page and i want to show them the same coordinates (x,x position).
How can I get the same point if I have to take in consideration that the div#container is not at same position as it was in my browser (considering screen resolution and scroll)?
I would use $.offset().top and $.offset().left of the parent div container, and calculate the difference from that to the current X and Y coordinates of the mouse cursor.
.offset() always refers to the document and not to the parent of the element.
For example:
$('#element').mousemove(function(e){
var client_x = e.pageX;
var client_y = e.pageY;
var elementOffset = $(this).offset();
client_x -= elementOffset.left;
client_y -= elementOffset.top;
// save x, y.
});
Then, on the other users display, show the coordinates after adding them to his offsets.
This doesn't seem possible because of the variables you mentioned in the question. Screen resolution is the main reason, but, also, it depends on how big their window is. At first, you might think that you could compute the mouse's position relative to fixed points, like divs shown (take Stack Overflow, for example, where the main container of the site doesn't resize with the browser window). But if their window is smaller than the container, you would be making some false assumptions about what they see.
That being said, you can always just compute the mouse position relative to fixed elements you know will be on the screenusing $.offset() and just assume they have their screen showing everything (or check $(window) size) and are using "normal" viewing conditions.
You can use the values returned by offset(), in your example:
$("div#container").offset().left
and
$("div#container").offset().top
to substract them to e.pageXand e.pageY.
offset() function gives you the matched element's position relative to the document (see the docs), so there's no problem if the users scroll down.
Example: http://jsfiddle.net/3jMRS/