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
Related
I'm creating a canvas with an overlay div to add markers on click and I want markers to change position when I pan zoom the canvas or resize the window. I'm using https://github.com/timmywil/panzoom to pan zoom.
The problem is when I convert mouse position to canvas coordinates it worked correctly but when I convert it back to screen position to render markers on overlay div, the result is not as same as initialized mouse position and recalculate marker's position on resize also not correct.
This canvas is fullscreen with no scroll.
width = 823; height = 411;
scale = 2; panX = 60; panY = 10;
mouse.pageX = 467; mouse.pageY = 144;
// {x: 475, y: 184} correct coords when I use ctx.drawImage(..) to test
canvasCoords = getCanvasCoords(mouse.pageX, mouse.pageY, scale);
// {x: 417, y: 124}
screenCoords = toScreenCoords(canvasCoords.x, canvasCoords.y, scale, panX, panY);
------------------------------
but with scale = 1; it worked correctly.
// convert mouse position to canvas coordinates
getCanvasCoords(pageX: number, pageY: number, scale: number) {
var rect = this.pdfInfo.canvas.getBoundingClientRect();
let x = (pageX - rect.left + this.scrollElement.scrollTop) / scale;
let y = (pageY - rect.top + this.scrollElement.scrollLeft) / scale;
return {
x: Number.parseInt(x.toFixed(0)),
y: Number.parseInt(y.toFixed(0)),
};
}
// convert canvas coords to screen coords
toScreenCoords(
x: number,
y: number,
scale: number
) {
var rect = this.pdfInfo.canvas.getBoundingClientRect();
let wx =
x * scale + rect.left - this.scrollElement.scrollTop / scale;
let wy =
y * scale + rect.top - this.scrollElement.scrollLeft / scale;
return {
x: Number.parseInt(wx.toFixed(0)),
y: Number.parseInt(wy.toFixed(0)),
};
}
getNewPos(x, oldV, newV) {
return (x * oldV) / newV;
}
// update screen coords with new screen width and height
onResize(old, new) {
this.screenCoordList.forEach(el => {
el.x = getNewPos(el.x, old.width, new.width);
el.y = getNewPos(el.y, old.height, new.height);
})
}
How to get it worked with scale and pan? if you know any library can do the job please recommend, thank you.
Here's a code snippet that seems to be working, you can probably adapt it for your purposes.
What I used was:
function toCanvasCoords(pageX, pageY, scale) {
var rect = canvas.getBoundingClientRect();
let x = (pageX - rect.left) / scale;
let y = (pageY - rect.top) / scale;
return toPoint(x, y);
}
and
function toScreenCoords(x, y, scale) {
var rect = canvas.getBoundingClientRect();
let wx = x * scale + rect.left + scrollElement.scrollLeft;
let wy = y * scale + rect.top + scrollElement.scrollTop;
return toPoint(wx, wy);
}
I'm just getting the mouse position from the window object. I'm may be mistaken, but I think this is why scrollLeft and scrollTop don't appear in toCanvasCoords (since the position is relative to the client area of the window itself, the scroll doesn't come into it). But then when you transform back, you have to take it into account.
This ultimately just returns the mouse position relative to the window (which was the input), so it's not really necessary to do the whole transformation in a roundabout way if you just want to attach an element to the mouse pointer. But transforming back is useful if you want to have something attached to a certain point on the canvas image (say, a to feature on the map) - which I'm guessing is something that you're going for, since you said that you want to render markers on an overlay div.
In the code snippet bellow, the red circle is drawn on the canvas itself at the location returned by toCanvasCoords; you'll notice that it scales together with the background.
I didn't use an overlay div covering the entire map, I just placed a couple of small divs on top using absolute positioning. The black triangle is a div (#tracker) that basically tracks the mouse; it is placed at the result of toScreenCoords. It serves as a way to check if the transformations work correctly. It's an independent element, so it doesn't scale with the image.
The red triangle is another such div (#feature), and demonstrates the aforementioned "attach to feature" idea. Suppose the background is a something like a map, and suppose you want to attach a "map pin" icon to something on it, like to a particular intersection; you can take that location on the map (which is a fixed value), and pass it to toScreenCoords. In the code snippet below, I've aligned it with a corner of a square on the background, so that you can track it visually as you change scale and/or scroll. (After you click "Run code snippet", you can click "Full page", and then resize the window to get the scroll bars).
Now, depending on what exactly is going on in your code, you may have tweak a few things, but hopefully, this will help you. If you run into problems, make use of console.log and/or place some debug elements on the page that will display values live for you (e.g. mouse position, client rectangle, etc.), so that you can examine values. And take things one step at the time - e.g. first get the scale to work, but ignore scrolling, then try to get scrolling to work, but keep the scale at 1, etc.
const canvas = document.getElementById('canvas');
const context = canvas.getContext("2d");
const tracker = document.getElementById('tracker');
const feature = document.getElementById('feature');
const slider = document.getElementById("scale-slider");
const scaleDisplay = document.getElementById("scale-display");
const scrollElement = document.querySelector('html');
const bgImage = new Image();
bgImage.src = "https://i.stack.imgur.com/yxtqw.jpg"
var bgImageLoaded = false;
bgImage.onload = () => { bgImageLoaded = true; };
var mousePosition = toPoint(0, 0);
var scale = 1;
function updateMousePosition(evt) {
mousePosition = toPoint(evt.clientX, evt.clientY);
}
function getScale(evt) {
scale = evt.target.value;
scaleDisplay.textContent = scale;
}
function toCanvasCoords(pageX, pageY, scale) {
var rect = canvas.getBoundingClientRect();
let x = (pageX - rect.left) / scale;
let y = (pageY - rect.top) / scale;
return toPoint(x, y);
}
function toScreenCoords(x, y, scale) {
var rect = canvas.getBoundingClientRect();
let wx = x * scale + rect.left + scrollElement.scrollLeft;
let wy = y * scale + rect.top + scrollElement.scrollTop;
return toPoint(wx, wy);
}
function toPoint(x, y) {
return { x: x, y: y }
}
function roundPoint(point) {
return {
x: Math.round(point.x),
y: Math.round(point.y)
}
}
function update() {
context.clearRect(0, 0, 500, 500);
context.save();
context.scale(scale, scale);
if (bgImageLoaded)
context.drawImage(bgImage, 0, 0);
const canvasCoords = toCanvasCoords(mousePosition.x, mousePosition.y, scale);
drawTarget(canvasCoords);
const trackerCoords = toScreenCoords(canvasCoords.x, canvasCoords.y, scale);
updateTrackerLocation(trackerCoords);
updateFeatureLocation()
context.restore();
requestAnimationFrame(update);
}
function drawTarget(location) {
context.fillStyle = "rgba(255, 128, 128, 0.8)";
context.beginPath();
context.arc(location.x, location.y, 8.5, 0, 2*Math.PI);
context.fill();
}
function updateTrackerLocation(location) {
const canvasRectangle = offsetRectangle(canvas.getBoundingClientRect(),
scrollElement.scrollLeft, scrollElement.scrollTop);
if (rectContains(canvasRectangle, location)) {
tracker.style.left = location.x + 'px';
tracker.style.top = location.y + 'px';
}
}
function updateFeatureLocation() {
// suppose the background is a map, and suppose there's a feature of interest
// (e.g. a road intersection) that you want to place the #feature div over
// (I roughly aligned it with a corner of a square).
const featureLoc = toScreenCoords(84, 85, scale);
feature.style.left = featureLoc.x + 'px';
feature.style.top = featureLoc.y + 'px';
}
function offsetRectangle(rect, offsetX, offsetY) {
// copying an object via the spread syntax or
// using Object.assign() doesn't work for some reason
const result = JSON.parse(JSON.stringify(rect));
result.left += offsetX;
result.right += offsetX;
result.top += offsetY;
result.bottom += offsetY;
result.x = result.left;
result.y = result.top;
return result;
}
function rectContains(rect, point) {
const inHorizontalRange = rect.left <= point.x && point.x <= rect.right;
const inVerticalRange = rect.top <= point.y && point.y <= rect.bottom;
return inHorizontalRange && inVerticalRange;
}
window.addEventListener('mousemove', (e) => updateMousePosition(e), false);
slider.addEventListener('input', (e) => getScale(e), false);
requestAnimationFrame(update);
#canvas {
border: 1px solid gray;
}
#tracker, #feature {
position: absolute;
left: 0;
top: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 10px solid black;
transform: translate(-4px, 0);
}
#feature {
border-bottom: 10px solid red;
}
<div>
<label for="scale-slider">Scale:</label>
<input type="range" id="scale-slider" name="scale-slider" min="0.5" max="2" step="0.02" value="1">
<span id="scale-display">1</span>
</div>
<canvas id="canvas" width="500" height="500"></canvas>
<div id="tracker"></div>
<div id="feature"></div>
P.S. Don't do Number.parseInt(x.toFixed(0)); generally, work with floating point for as long as possible to minimize accumulation of errors, and only convert to int at the last minute. I've included the roundPoint function that rounds the (x, y) coordinates of a point to the nearest integer (via Math.round), but ended up not needing to use it at all.
Note: The image below is used as the background in the code snippet, to serve as a reference point for scaling; it is included here just so that it is hosted on Stack Exchange's imgur.com account, so that the code is not referencing a (potentially volatile) 3rd-pary source.
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
I am having difficulty of getting the mouse coordinates and the hero's coordinates.
When I click on the bottom right corner of the canvas I get a result of x = 641, y = 386 for the mouse click. The enemy coordinate system is 100% accurate.
It seems that the enemy coordinate system is different than the mouse coordinate system. I want them to be on a single coordinate system. Thanks for your help!
This is the initialization of canvas:
<canvas id="canvas" width = "1664" height = "1000" style = "border:1px solid gray; width: 640px; height 480px;"> </canvas>
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
var mousePos = getMousePos(Context.canvas,e);
//Use mousePos.x or mousePos.y to get the coordinates of the mouse click
var mx, my;
if(e.offsetX) {
mx = e.offsetX;
my = e.offsetY;
}
else if(e.layerX) {
mx = e.layerX;
my = e.layerY;
}
Gun.shoot()
for (var i = EnemyManager.enemies.length - 1; i >= 0; i--) {
var enemy = EnemyManager.enemies[i]
console.log("Enemy: " + enemy.x + " " + enemy.y)
console.log("Mouse: " + mousePos.x + " " + mousePos.y)
if ((enemy.x < mx) && (enemy.y < my) && ( (enemy.x + enemy.width) > mx ) && (enemy.y + enemy.height > my)) {
alert("Target HIT")
}
};
})*
I've had the same problem using canvas (especially it needs to work when scrolled down on the page, offset from the top of the page by arbitrary divs, and at different zoom levels). I've found the approach in this answer to be the most robust as a replacement to that getMousePos function.
You are receiving different coordinates probably because you are using two different methods. In the player's position, you are using offsetX instead of mousePos.X (enemy.X in comparison)
Also, since you are defining the return of getMousePos as X and Y, you need to access them as X and Y. So:
var mx, my;
mx = mousePos.X;
my = mousePos.Y;
EDIT: You may also find this link helpful.
You are clicking outside of canvas see my example, http://jsfiddle.net/gn0pkkra/.
document.getElementById('wrapper').addEventListener('click', on_canvas_click, false);
You can compute the exact trajectory even if you are clicking outside of canvas. Then Cartesian coordinates will have the center (0,0) on you game cannon position in canvas. Compute cannon/gun offset over top left corner of canvas (real 0,0) and use those offsets to detect your trajectories.
Clicking on buttom right of the canvas, or even outside of it's impossible to get negative values.
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){
...
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);
});