Canvas pixel coordinate offset after scroll bar is used - javascript

I'm using a canvas at the top of a page. Im writing out the pixel coordinates from the canvas at a mousemove event. Normally, the most bottom Y-value is equal to the canvas height, i e. 700px. But after scrollbar is used to scroll down a bit on the page, the bottom y-coordinate in the canvas will change accordingly to say 400px instead.
document.getElementById("mapcanvas").addEventListener("mousemove", getPosition, false);
function getPosition(event)
{
var x = event.x;
var y = event.y;
var canvas = document.getElementById("mapcanvas");
x -= canvas.offsetLeft;
y -= canvas.offsetTop;
document.getElementById("canvascoords").innerHTML = "Canvascoords: "+ "x=" + x + ", y=" + y;
}
... Where "mapcanvas" is my div holding the canvas.
Any ideas of making the y-coordinate independent from usage of scroll bar so that the lower y-coordinate always i 700px?

As you've discovered, canvas.offsetLeft & canvas.offsetTop do not account for scrolling.
To account for scrolling, you can use canvas.getBoundingClientRect
var BB=canvas.getBoundingClientRect();
var x=event.clientX-BB.left;
var y=event.clientY-BB.top;
BTW, you might want to fetch a reference to the canvas element just once outside your getPosition() function instead of fetching it repeatedly inside getPosition().
var canvas = document.getElementById("mapcanvas");
function getPosition(event){
...

Related

Mouse movement on a JavaScript Canvas that works with CSS [duplicate]

I'm trying to draw with the mouse over a HTML5 canvas, but the only way that it seems to work well is if the canvas is in the position 0,0 (upper left corner) if I change the canvas position, for some reason it doesn't draw like it should. Here is my code.
function createImageOnCanvas(imageId){
document.getElementById("imgCanvas").style.display = "block";
document.getElementById("images").style.overflowY= "hidden";
var canvas = document.getElementById("imgCanvas");
var context = canvas.getContext("2d");
var img = new Image(300,300);
img.src = document.getElementById(imageId).src;
context.drawImage(img, (0),(0));
}
function draw(e){
var canvas = document.getElementById("imgCanvas");
var context = canvas.getContext("2d");
posx = e.clientX;
posy = e.clientY;
context.fillStyle = "#000000";
context.fillRect (posx, posy, 4, 4);
}
The HTML part
<body>
<div id="images">
</div>
<canvas onmousemove="draw(event)" style="margin:0;padding:0;" id="imgCanvas"
class="canvasView" width="250" height="250"></canvas>
I have read there's a way of creating a simple function in JavaScript to get the right position, but I have no idea about how to do it.
The Simple 1:1 Scenario
For situations where the canvas element is 1:1 compared to the bitmap size, you can get the mouse positions by using this snippet:
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
Just call it from your event with the event and canvas as arguments. It returns an object with x and y for the mouse positions.
As the mouse position you are getting is relative to the client window you’ll have to subtract the position of the canvas element to convert it relative to the element itself.
Example of integration in your code:
// put this outside the event loop..
var canvas = document.getElementById("imgCanvas");
var context = canvas.getContext("2d");
function draw(evt) {
var pos = getMousePos(canvas, evt);
context.fillStyle = "#000000";
context.fillRect (pos.x, pos.y, 4, 4);
}
Note: borders and padding will affect position if applied directly to the canvas element so these needs to be considered via getComputedStyle() – or apply those styles to a parent div instead.
When Element and Bitmap are of different sizes
When there is the situation of having the element at a different size than the bitmap itself, for example, the element is scaled using CSS or there is pixel-aspect ratio etc. you will have to address this.
Example:
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect(), // abs. size of element
scaleX = canvas.width / rect.width, // relationship bitmap vs. element for x
scaleY = canvas.height / rect.height; // relationship bitmap vs. element for y
return {
x: (evt.clientX - rect.left) * scaleX, // scale mouse coordinates after they have
y: (evt.clientY - rect.top) * scaleY // been adjusted to be relative to element
}
}
With transformations applied to context (scale, rotation etc.)
Then there is the more complicated case where you have applied transformation to the context such as rotation, skew/shear, scale, translate etc. To deal with this you can calculate the inverse matrix of the current matrix.
Newer browsers let you read the current matrix via the currentTransform property and Firefox (current alpha) even provide an inverted matrix through the mozCurrentTransformInverted. Firefox however, via mozCurrentTransform, will return an Array and not DOMMatrix as it should. Neither Chrome, when enabled via experimental flags, will return a DOMMatrix but a SVGMatrix.
In most cases however you will have to implement a custom matrix solution of your own (such as my own solution here – free/MIT project) until this get full support.
When you eventually have obtained the matrix regardless of path you take to obtain one, you’ll need to invert it and apply it to your mouse coordinates. The coordinates are then passed to the canvas which will use its matrix to convert it to back wherever it is at the moment.
This way the point will be in the correct position relative to the mouse. Also here you need to adjust the coordinates (before applying the inverse matrix to them) to be relative to the element.
An example just showing the matrix steps:
function draw(evt) {
var pos = getMousePos(canvas, evt); // get adjusted coordinates as above
var imatrix = matrix.inverse(); // get inverted matrix somehow
pos = imatrix.applyToPoint(pos.x, pos.y); // apply to adjusted coordinate
context.fillStyle = "#000000";
context.fillRect(pos.x-1, pos.y-1, 2, 2);
}
An example of using currentTransform when implemented would be:
var pos = getMousePos(canvas, e); // get adjusted coordinates as above
var matrix = ctx.currentTransform; // W3C (future)
var imatrix = matrix.invertSelf(); // invert
// apply to point:
var x = pos.x * imatrix.a + pos.y * imatrix.c + imatrix.e;
var y = pos.x * imatrix.b + pos.y * imatrix.d + imatrix.f;
Update: I made a free solution (MIT) to embed all these steps into a single easy-to-use object that can be found here and also takes care of a few other nitty-gritty things most ignore.
You can get the mouse positions by using this snippet:
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: (evt.clientX - rect.left) / (rect.right - rect.left) * canvas.width,
y: (evt.clientY - rect.top) / (rect.bottom - rect.top) * canvas.height
};
}
This code takes into account both changing coordinates to canvas space (evt.clientX - rect.left) and scaling when canvas logical size differs from its style size (/ (rect.right - rect.left) * canvas.width see: Canvas width and height in HTML5).
Example: http://jsfiddle.net/sierawski/4xezb7nL/
Source:
jerryj comment on http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
You need to get the mouse position relative to the canvas
To do that you need to know the X/Y position of the canvas on the page.
This is called the canvas’s “offset”, and here’s how to get the offset. (I’m using jQuery in order to simplify cross-browser compatibility, but if you want to use raw javascript a quick Google will get that too).
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
Then in your mouse handler, you can get the mouse X/Y like this:
function handleMouseDown(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
}
Here is an illustrating code and fiddle that shows how to successfully track mouse events on the canvas:
http://jsfiddle.net/m1erickson/WB7Zu/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
function handleMouseDown(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
$("#downlog").html("Down: "+ mouseX + " / " + mouseY);
// Put your mousedown stuff here
}
function handleMouseUp(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
$("#uplog").html("Up: "+ mouseX + " / " + mouseY);
// Put your mouseup stuff here
}
function handleMouseOut(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
$("#outlog").html("Out: "+ mouseX + " / " + mouseY);
// Put your mouseOut stuff here
}
function handleMouseMove(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
$("#movelog").html("Move: "+ mouseX + " / " + mouseY);
// Put your mousemove stuff here
}
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});
}); // end $(function(){});
</script>
</head>
<body>
<p>Move, press and release the mouse</p>
<p id="downlog">Down</p>
<p id="movelog">Move</p>
<p id="uplog">Up</p>
<p id="outlog">Out</p>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
The easiest way to compute the correct mouse click or mouse move position on a canvas event is to use this little equation:
canvas.addEventListener('click', event =>
{
let bound = canvas.getBoundingClientRect();
let x = event.clientX - bound.left - canvas.clientLeft;
let y = event.clientY - bound.top - canvas.clientTop;
context.fillRect(x, y, 16, 16);
});
If the canvas has padding-left or padding-top, subtract x and y via:
x -= parseFloat(style['padding-left'].replace('px'));
y -= parseFloat(style['padding-top'].replace('px'));
Refer this question: The mouseEvent.offsetX I am getting is much larger than actual canvas size
.I have given a function there which will exactly suit in your situation

Unable to draw in canvas after moving its position in the page [duplicate]

I am trying to use getBoundingClientRect to get the coordinates of my click on canvas, but am always getting the same result.
My code is here: http://fiddle.jshell.net/nH74F/1/
As you can see i always get 8,8
No idea why, is there another way to get this info?
That's because you always use the absolute position of the element returned by getBoundingClientRect, and not the mouse position.
Try this instead:
canvas.addEventListener('click', function(e) { // use event argument
var rect = canvas.getBoundingClientRect(); // get element's abs. position
var x = e.clientX - rect.left; // get mouse x and adjust for el.
var y = e.clientY - rect.top; // get mouse y and adjust for el.
alert('Mouse position: ' + x + ',' + y);
...
Modified fiddle

Click event position (Y) is too high

I have a canvas element and when i click on it i get the click position with e.clientX(Y) or e.screenX(Y). Something strange is happening. Y value is always too high. Please look at this image: http://img840.imageshack.us/img840/268/eventq.jpg. Any ideia why is it so high?
You just need to take the ClientX and Y and subtract the position of the canvas from them.
This example is unnecessarily verbose, just to show the steps:
var canvas = document.getElementById('game');
var canvasX, canvasY;
canvas.addEventListener('click', function(event) {
canvasX = canvas.offsetLeft;
canvasY = canvas.offsetTop;
var eventX = event.clientX;
var eventY = event.clientY;
var relX = eventX - canvasX;
var relY = eventY - canvasY;
alert('X = ' + relX + ', Y = ' + relY);
});
Working sample: http://jsfiddle.net/JfhJF/
I'm pretty sure you can tell what's happening. You do not have the coordinates relative to your canvas, but relative to your viewport. It also depends on your browser whether or not they include padding.
Convert them to normal coordinates. In your case this involves substracting the offset of the canvas.

Incorrect layerX/layerY values?

I'm trying to implement a function as a part of a firefox add-on with canvas which gives the user the ability to draw.
function draw(event,context,drawit) {
var drawx = event.layerX;
var drawy = event.layerY;
if (!drawit) {
context.beginPath();
context.strokeStyle='rgb(0,255,0)';
context.lineWidth=1;
context.moveTo(drawx,drawy);
drawit = true;
} else {
context.lineTo(drawx,drawy);
context.stroke();
}
};
This works, but there seems to be a difference between the result of layerX/layerY and the line drawn. It's only possible to draw in the upper left part of the canvas element. When the mouse pointer reaches about the half of the element the line doesn't go further.
I already checked the position of the elements in Firebug and it seems ok: the canvas is inside a div-element and both have a defined width of 100%, while the drawing ends at about 50% of the element. It also works to set the values manually so that the line is also drawn in the right part of the canvas element.
Does anyone have an idea what is going wrong?
When the mouse pointer reaches about the half of the element the line doesn't go further.
This could happen for a lot of reasons. Mis-translated context (from rotation/translation/scaling) and mismatched canvas size (what you wrote it as in html and what you're considering it in code).
Are you certain that LayerX and LayerY are getting you the right mouse co-ordinates? What exactly do you mean by "difference" between layerx/y and whats drawn? Is there an offset? I ask because my mouse code is a bit more complex:
// Sets mx,my to the mouse position relative to the canvas
// unfortunately this can be tricky, we have to worry about padding and borders
function getMouse(e) {
var element = canvas, offsetX = 0, offsetY = 0;
if (element.offsetParent) {
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
} while ((element = element.offsetParent));
}
// Add padding and border style widths to offset
offsetX += stylePaddingLeft;
offsetY += stylePaddingTop;
offsetX += styleBorderLeft;
offsetY += styleBorderTop;
mx = e.pageX - offsetX;
my = e.pageY - offsetY
}

Html Canvas Resizing And Coordinate Space

Note: I'm using Google Chrome
Currently I have this test page
http://www.evecakes.com/doodles/canvas_size_issue.htm
It should draw a rectangle right below the mouse cursor, but there are some really weird scaling issues which is causing it to draw at a varying offset. I think it's because the canvas coordinate space is not increasing along with its html size. Is there a way to increase that coordinate space size?
Here's the javascript function
$('#mapc').mousemove(function (event) {
var canvas = $('#mapc');
var ctx = canvas[0].getContext('2d');
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect(event.clientX, event.clientY, 55, 50);
document.title = event.clientX + " --- " + event.clientY;
});
Set the width and height HTML attributes of the canvas. Right now, it's assuming its default width and the CSS is just stretching it, and it appears as if it is scaling.
A side-note, you can use this in the mousemove event - it is a reference to #mapc element, so you won't have to query the DOM on every mouse move.
var offset = $('#mapc').offset();
$('#mapc').mousemove(function (event) {
var ctx = this.getContext('2d');
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect(event.pageX - offset.left, event.pageY - offset.top, 1, 1);
});

Categories

Resources