There was no offsetX in e.touches[0] when I tried on iPad. Does anyone know where I can get this value of offsetX?
The correct answer based on the comments in the suggested answer:
e.offsetX = e.touches[0].pageX - e.touches[0].target.offsetLeft;
e.offsetY = e.touches[0].pageY - e.touches[0].target.offsetTop;
This ignores any transformations such as rotations or scaling. Also be sure to check if there are any touches.
You can use clientX or pageX, see here
Thanks, #Kontiki - this is the solution that finally fixed things for me:
if("touchmove" == e.type)
{
let r = canvas.getBoundingClientRect();
currX = e.touches[0].clientX - r.left;
currY = e.touches[0].clientY - r.top;
}
else
{
currX = e.offsetX;
currY = e.offsetY;
}
The page - offset / client approach did not work for me. There was still an offset. I found this other solution that works perfectly:
let r = canvas.getBoundingClientRect();
let x = e.touches[0].pageX - r.left;
let y = e.touches[0].pageY - r.top;
Was having similar issue on binding event-handler using jQuery's .on function on canvas element (Don't know the reason).
I resolved it by binding event-handler using addEventListener. The event object in the handler has offsetX and offsetY defined with proper values.
Hope it helps...
Related
I want to get the "inner offset", or rather the inner coordinates of a clicked element via the Javascript Click-Event. As you can see in the image, I need the Offset X and Offset Y. Is there any property which gives me this information?
Using plain Javascript and the "mousedown" and "mousemove" event.
You can use offsetX and offsetY properties of the MouseEvent object.
document.addEventListener('mousedown', function(e) {
console.log(e.offsetX, e.offsetY);
});
the values you want are offsetX and offsetY values
document.addEventListener('click', function(e) {
const element = document.getElementById("child");
const offsetX = (e.clientX - element.offsetLeft)
const offsetY = (e.clientY - element.offsetTop)
}
I'm trying to create a simple animation where some particles animation follow the cursor, but i'm having trouble with it.
I've created a fiddle to replicate the issue : Example on JSFiddle
Right now my particles appear, but when you move the cursor over the section, they suddenly disappear. I know my error comes from my mousemove() function, but i can't figure out what is wrong with it..
here is my mousemove function :
function mouseMove(e) {
var posx = posy = 0;
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
}
else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
target.x = posx;
target.y = posy;
}
Your mouse coordinate X, Y is relative to the top/left corner of the web page, probably mousemove event is attached to document, not to the canvas. Attach the mosemove event to the canvas
document.getElementById('services-canvas').addEventListener('mousemove', mouseMove);
And use the elemnt ofset:
target.x = e.offsetX;
target.y = e.offsetY;
If you would like the mouse to be in the centre of figure, then use e.offsetY-something where something is half of height of figure
So your particles do actually follow the mouse from what I have seen in . However, it seems that they are way lower in the y position that you would expect.You need to do this to make it work properly:
target.y = posy -300;
I have tried it and it worked with this little change. Hope this helped :D
I am trying to use canvas so that with a mouse a person can write their signature. Everything works until I stretch or scroll the screen then it draws the line in a different place away from the mouse.
The Code:
function onMouseUp(event) {
'use strict';
mousePressed = false;
}
function onMouseMove(event) {
'use strict';
if (mousePressed) {
event.preventDefault();
mouseX = event.clientX - can.offsetLeft - mleft;
mouseY = event.clientY - can.offsetTop - mtop;
ctx.lineTo(mouseX, mouseY);
ctx.stroke();
}
}
function onMouseDown(event) {
'use strict';
mousePressed = true;
mouseX = event.clientX - can.offsetLeft - mleft;
mouseY = event.clientY - can.offsetTop - mtop;
ctx.beginPath();
ctx.moveTo(mouseX, mouseY);
}
can.addEventListener('mousemove', onMouseMove, false);
can.addEventListener('mousedown', onMouseDown, false);
can.addEventListener('mouseup', onMouseUp, false);
HTML looks like:
<canvas id="signature" width="567" height="150"></canvas>
event.clientX/Y is relative to the top left corner of the viewport. So scroll isn't taken into account. event.pageX/Y is relative to the document. So it is the position on screen that the event happened including scroll. You can change all references to clientX to pageX and clientY to pageY and it should work.
Explanation of each screen/page/client XY.
It seems like you just need a more reliable method of getting the relative coordinates when the page reflows.
#RyanArtecona wrote the following function in response to this question about mouse coordinates relative to a canvas:
function relMouseCoords(event){
var totalOffsetX = 0;
var totalOffsetY = 0;
var canvasX = 0;
var canvasY = 0;
var currentElement = this;
do{
totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
}
while(currentElement = currentElement.offsetParent)
canvasX = event.pageX - totalOffsetX;
canvasY = event.pageY - totalOffsetY;
return {x:canvasX, y:canvasY}
}
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
This is convenient because it adds a function to get the relative coordinates right onto the prototype of the HTMLCanvasElement function, which means you can just pass in a reference to the canvas you want to use, and get coordinates relative to it.
Using this you could rewrite your mousedown function (and you'll want to do the others as well, but just for an example) as follows:
function onMouseDown(event) {
'use strict';
mousePressed = true;
// get a reference to the 'signature' canvas
var canvas = document.getElementById('signature');
// this returns an object with 'x' and 'y' properties
var mouse = canvas.relMouseCoords(event)
ctx.beginPath();
// use the coordinates you got
ctx.moveTo(mouse.x, mouse.y);
}
Change these two lines
mouseX = event.clientX - can.offsetLeft - mleft;
mouseY = event.clientY - can.offsetTop - mtop;
to
mouseX = event.offsetX || event.layerX;
mouseY = event.offsetY || event.layerY;
in both of your handlers. The browser can handle the relative coordinates for you without having to do any special math. offsetX/Y seems Chrome/IE specific and layerX/Y gets you Firefox support.
Here is a jsfiddle. I made a couple slight declaration changes to get your use stricts to work, since we seem to be missing a little of the code.
Add the scroll offset of the screen to the mouseX and mouseY. with jQuery it would look like this
mouseX = event.clientX - can.offsetLeft - mleft + $(window).scrollLeft;
mouseY = event.clientY - can.offsetTop - mtop + $(window).scrollTop;
This is not a simple answer. The best way to figure this out is to start with a good foundation and then make incremental changes for your situation.. The best article I have seen on this subject is from the Internet Explorer Team:
Handling Multi-touch and Mouse Input in All Browsers
This article will give you a great foundation to capturing mouse input correctly.
I cannot make this ray to follow my mouse cursor.
It's always inverse on X axis.
I think that the problem is with my atan function, but since my trigonometry is fairly low, I have no idea how to fix this one.
This is how I calculate radians:
var dx = target.x - center.x;
var dy = target.y - center.y;
var rad = Math.atan2(dx, dy);
var dx = center.x - target.x;
Fiddle
The fiddle above doesn't work in Firefox as FF doesn't natively support event.offsetX/Y for mouse events, I assume your normalization code was cut off when making the minimalistic example but I'll leave my normalization code here for future reference:
function normOffset(event) {
if (typeof event.offsetX === 'undefined' || typeof event.offsetY === 'undefined') {
var targetOffset = $(event.target).offset();
event.offsetX = event.pageX - targetOffset.left;
event.offsetY = event.pageY - targetOffset.top;
}
}
Then just pass the jQuery event object to it.
Fiddle
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script language="javascript">
function main(){
var canvas = document.getElementById("canvas");
canvas.addEventListener("mousemove", function(e){
if (!e) e = window.event;
var ctx = canvas.getContext("2d");
var x = e.offsetX;
var y = e.offsetY;
ctx.fillRect(x, y, 1, 1);
});
}
</script>
</head>
<body onload="main();">
<div style="width: 800px; height: 600px; -webkit-transform: scale(0.75,0.75); -moz-transform: scale(0.75,0.75)">
<canvas id="canvas" width="400px" height="400px" style="background-color: #cccccc;"></canvas>
</div>
</body>
</html>
Please consider the above quick and dirty example.
Please notice that my canvas is contained by a div having a scale transform applied.
The above code works perfectly on any webkit based browser. While moving the mouse it draws points on the canvas.
Unfortunately it doesn't in Firefox as its event model does not support the offsetX / Y properties.
How can I transform mouse coordinates from (perhaps) event.clientX (which is supported in firefox too) into canvas relative coordinates taking into account canvas position, transform etc?
Thanks, Luca.
From a JQuery bug tracker page - a nice polyfill is this:
var offX = (e.offsetX || e.pageX - $(e.target).offset().left);
.. where e is the event returned from a jquery event. Obviously, only if you've got Jquery already on your project, otherwise will have to do the offset() stuff manually.
Try layerX, layerY
var x = (e.offsetX === undefined) ? e.layerX : e.offsetX;
var y = (e.offsetY === undefined) ? e.layerY : e.offsetY;
FIDDLE
Unfortunately no solution worked for me.
I found a good implementation here:
var target = e.target || e.srcElement,
rect = target.getBoundingClientRect(),
offsetX = e.clientX - rect.left,
offsetY = e.clientY - rect.top;
e.offsetX = offsetX;
e.offsetY = offsetY;
Unfortunately offsetX and layerX are not exactly the same as offsetX is the offset within the current element but layerX is the offset from the page. Below is a fix I am currently using for this:
function fixEvent(e) {
if (! e.hasOwnProperty('offsetX')) {
var curleft = curtop = 0;
if (e.offsetParent) {
var obj=e;
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while (obj = obj.offsetParent);
}
e.offsetX=e.layerX-curleft;
e.offsetY=e.layerY-curtop;
}
return e;
}
There's a bug in Musa's solution: think what happens if e.offsetX === 0 and e.layerX === undefined...
var x = e.offsetX || e.layerX; // x is now undefined!
A more robust version is as follows:
var x = e.hasOwnProperty('offsetX') ? e.offsetX : e.layerX;
var y = e.hasOwnProperty('offsetY') ? e.offsetY : e.layerY;
Or, because we can assume that if offsetX is defined, offsetY will be too:
var hasOffset = e.hasOwnProperty('offsetX'),
x = hasOffset ? e.offsetX : e.layerX,
y = hasOffset ? e.offsetY : e.layerY;
offset actually doesn't translate directly into layer; the offset property doesn't account for the element's margin. The code below should account for this.
function(e) {
var x = e.offsetX, y = e.offsetY;
if(e.hasOwnProperty('layerX')) {
x = e.layerX - e.currentTarget.offsetLeft;
y = e.layerY - e.currentTarget.offsetTop;
}
}
None of the non-jquery versions work completely for various reasons. With your help however i got this to work:
if(!event.hasOwnProperty('offsetX')) {
event.offsetX = event.layerX - event.currentTarget.offsetLeft;
event.offsetY = event.layerY - event.currentTarget.offsetTop;
}
I found that all answers posted here except the last two answers by EnotionZ and laz brannigan (previously with zero votes each) where wrong in cases where several elements were contained within a div. In my case I have several canvas elements inside of a single div and I am listening to each canvas separately.
After considerable trial and error the final correct answer I came to, which works perfectly and identically for me in FireFox and Chrome is as follows:
//inside my mouse events handler:
var anOffsetX = (inEvent.offsetX !== undefined) ? inEvent.offsetX : (inEvent.layerX - inEvent.target.offsetLeft);
var anOffsetY = (inEvent.offsetY !== undefined) ? inEvent.offsetY : (inEvent.layerY - inEvent.target.offsetTop);
Presumably this will also work for margins and such as EnotionZ indicated in his post, but I have not tried that.
But , what you will do if there is not layerX,layerY fields ?
var xe=e.offsetX,ye=e.offsetY;
if(!xe){
xe=e.clientX - $(e.target).offset().left;
}
if(!ye){
ye= e.clientY - $(e.target).offset().top;
}
This question needs an updated answer.
First, as I mentioned in a comment, regarding all of the older answers using layerX and layerY:
"This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future."
(Source: developer.mozilla.org/en-US/docs/Web/API/UIEvent/layerX)
Second, I've discovered that when you console.log(event) in Firefox, offsetX and offsetY show 0, but when you console.log(event.offsetX), it is not 0. So be careful because you may be being lied to.
This behavior is explained here:
Logging objects
Don't use console.log(obj), use console.log(JSON.parse(JSON.stringify(obj))).
This way you are sure you are seeing the value of obj at the moment you log it. Otherwise, many browsers provide a live view that constantly updates as values change. This may not be what you want.
(Also note that JSON.stringify() will not do what you want here... It doesn't stringify the entire event object.)
OffsetX/Y behavior is different in FireFox, Chrome/Edge. You may need calculate the value by adding the code snippet:
const offsetX = evt.clientX - this.myRef.current.getBoundingClientRect().left;
const offsetY = evt.clientY - this.myRef.current.getBoundingClientRect().top;
The myRef is ref of its nearest parent element. So the offset will be the distance between this element (clientX) minus the one of its parent.
If some still needs a solutions this one works perfect in Firefox:
var x = e.offsetX || e.originalEvent.layerX;
var y = e.offsetY || e.originalEvent.layerY;