Obtain co-ordinates of an HTML5 canvas on a page - javascript

I have a a canvas inside another canvas.
<canvas id ='canvas2' height="718" width="1316"></canvas>
its css is something
#canvas2{
position:absolute;
width :95%;
height:90%;
top:5%;
left:2.5%;
background: #ffff56;
cursor:pointer;
}
next I have drawn some rectangles on it. I need to colour those with mouse click. I used an action listener.
var canvas = document.getElementById("canvas2");
var ctx = canvas.getContext("2d");
canvas.addEventListener("mousedown", doMouseDown, false);
var $canvas = $("#canvas2");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
function doMouseDown(event){
event.preventDefault();
event.stopPropagation();
var x= parseInt(event.clientX - offsetX);
var y = parseInt(event.clientY - offsetY);
}
But this is not the right way I know as I am getting all the wrong canvas co-ordinates on x and y.
Can someone show the right way?

Has your canvas been scrolled?
If yes, then you also need to account for the distance the canvas has been scrolled in the browser.
You might check out canvas.getBoundingClientRect() as a way to get the canvas position with the scrolling accounted for:
function handleMousemove(e){
e.preventDefault();
e.stopPropagation();
// if the canvas is stationary (not scrolling) then you can do
// .getBoundingClientRect once at the start of the app
var BB=canvas.getBoundingClientRect();
// calc mouse position based on the bounding box
var mouseX=parseInt(e.clientX-BB.left);
var mouseY=parseInt(e.clientY-BB.top);
console.log(mouseX+"/"+mouseY);
}

Related

Multiple canvas polygon detection map using isPointInPath() and responsive design

I have an array of canvas objects that draw correctly. i have three problems:
Offset. I have tested the code below in JS fiddle and it works, but when i export it my web page, the variables get skewed. The detection happens, but not in the right place. the page width is set in CSS, and the actual canvas area is centered using a margin:0 auto call, however it is smaller than the page width.
<canvas id="canvas" width="780" height="690" style="position:absolute;"></canvas>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
var $results = $("#results");
// define the polygon items
var polyArray = new Array (6);
polyArray [0] =[{x:50,y:236}, {x:200,y:115}, {x:350,y:50}, {x:350,y:300}, {x:232,y:325}, {x:75,y:300}];
polyArray [1] =[{x:350,y:55}, {x:350,y:300}, {x:510,y:300}, {x:510,y:205}, {x:578,y:172}, {x:690,y:96}, {x:650,y:17}];
polyArray [2] =[{x:510,y:300}, {x:510,y:200}, {x:715,y:113}, {x:780,y:200}, {x:780,y:485}, {x:625,y:468}, {x:605,y:456}, {x:605,y:428}];
polyArray [3] =[{x:0,y:446}, {x:284,y:320}, {x:255,y:540}, {x:240,y:566}, {x:73,y:600}, {x:0,y:565}];
polyArray [4] =[{x:355,y:305}, {x:510,y:305}, {x:604,y:423}, {x:604,y:460}, {x:628,y:484}, {x:610,y:513}, {x:587,y:468}, {x:537,y:426}, {x:500,y:400}, {x:447,y:424}, {x:312,y:365}, {x:307,y:314 }];
polyArray [5] =[{x:350,y:425}, {x:415,y:421}, {x:455,y:434}, {x:495,y:411}, {x:550,y:444}, {x:618,y:590}, {x:570,y:616}, {x:359,y:597}, {x:333,y:522}];
// call the function to draw all the objects in the array
define(polyArray);
// call through the array to draw the objects
function define(polygon) {
ctx.beginPath();
for (var i = 0; i < polygon.length; i++) {
ctx.moveTo(polygon[i][0].x, polygon[i][0].y);
for (var j = 1; j < polygon[i].length; j++) {
ctx.lineTo(polygon[i][j].x, polygon[i][j].y);
}
ctx.fill();
}
ctx.closePath();
}
function hitTest(polygon) {
// redefine the polygon
define(polygon);
// ask isPointInPath to hit test the mouse position
// against the current path
return (ctx.isPointInPath(mouseX, mouseY));
}
function handleMouseMove(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// check if the mouse is inside the polygon
var isInside = hitTest(polyArray);
if (isInside) {
canvas.style.cursor = 'pointer';
$results.text("Mouse is inside the area);
} else {
canvas.style.cursor = 'default';
$results.text("Outside");
}
}
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
Detecting which object has been hovered over. What needs to happen is on hover of one the array shapes should effect some CSS/JS. How can i assign an ID variable and detect it?
when i bring responsive design into the equation i'm a bit stuck for how to incorporate this offset and the poly co-ords to scale appropriately.
Any point in the right direction would be greatly appreciated.
Question#1: Getting accurate mouse position after the canvas has moved
Whenever you move your canvas (fex: margin: 0 auto), you must recalculate your offsetX and offsetY values:
If you manually change the canvas element's CSS (fex: canvas.style.margin='50px' inside javascript), then you must also manually call reOffset().
// cache the canvas's offset positions since the
// offset positions are used often
var offsetX,offsetY;
// call this once at the beginning of your app
// and whenever you change the canvas's position on the page
// (eg call when you change margins, scroll, etc)
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
// have the browser auto-reset offsetX & offsetX when
// the viewport scrolls or resizes
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
Question#2 Detecting hovers & blurs over your polygons
Your hitTest function will test if the mouse is currently inside a specified polygon. So inside handleMousemove you could call hitText for each of the polygons inside your polyArray.
Keep a flag variable indicating the index# of the last polygon the mouse was inside (or -1 to indicate the mouse was outside all polygons. When your flag variable value changes, you know there has been either a hover-event or a blur-event. Compare the last and current flag variables to determine which polygon is now hovered or blurred.
Question#3 Incorporating a responsive design
Mouse coordinates reported by the browser into e.clientX and e.clientY are always in unscaled values relative to the browser viewport.
So if you:
Click the mouse and use e.clientX/e.clientY to determine the mouse is at [100,100],
Scale your canvas: context.scale(2,2),
And reclick without moving the mouse from its original [100,100] position,
Then:
Using e.clientX/e.clientY to detect the mouse coordinates will still report the position as [100,100] even if the canvas has been scaled and the mouse is at [200,200] relative to the scaled canvas.
The fix:
You must scale the browser's reported mouse position to match the scaling factor of the canvas:
// Determine how much you want to scale the canvas
var scaleFactor=2.00;
// scale the canvas
context.scale(scaleFactor,scaleFactor);
// also scale the mouse position reported by the browser
mouseX=parseInt(e.clientX-offsetX)*scaleFactor;
mouseY=parseInt(e.clientY-offsetY)*scaleFactor;

Is it possible to create `html5 canvas` dynamically with the mouse?

Is it possible to create HTML5 canvas dynamically with the mouse?
For example: I want to use my mouse to draw HTML5 canvas then load content into the different canvas.
3 hour I search over the internet for a demo and I found nothing about that possibility.
You can create a canvas on the mouse location
http://jsfiddle.net/v4nm487b/
document.onmousedown=mouseDown;
document.onmouseup=mouseUp;
var x1,y1;
function mouseDown(e){
x1=e.clientX;
y1=e.clientY;
}
function mouseUp(e){
var can = document.createElement("CANVAS");
can.style.position = "absolute";
can.style.left = x1+"px";
can.style.top = y1+"px";
can.width=Math.abs(x1-e.clientX);
can.height=Math.abs(y1-e.clientY);
can.style.border="1px solid black"
document.body.appendChild(can);
}
But what do you mean with load the content?
edit: dynamic content
You can give the canvas an id and use that to draw on it
In this exemple the created canvases have the id canN (can0,can1,canN)
If we presse 0 on the numpad (keycode 48), I fill canvas can(48-48)= can0
This ofcourse only work up to 9 but it does prove it can(vas) be done
http://jsfiddle.net/v4nm487b/6/
I can't say this for certain, as I've never tried it, but you might look into the mousedown and mouseup events. You could create an element that the user can drag in and listen for those events on it like this:
var startX, startY, endX, endY;
$('#drag')
.mousedown(function(e) {
startX = e.pageX;
startY = e.pageY;
$('#start').text(startX + ", " + startY);
})
.mouseup(function(e) {
endX = e.pageX;
endY = e.pageY;
$('#end').text(endX + ", " + endY);
});
#drag {
width: 100%;
height: 500px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="start"></div>
<div id="end"></div>
<div id="drag"></div>
That gives you the start and end positions of the drag, you can then append a canvas element with some absolute positioning based on those coordinates. Hope this helps!

Mouse Proximity to object Javascript

I'm struggling with this for a while now. I'm drawing a grid on the canvas. I add a mousemove eventHandler and track the mouseX and mouseY positions. I would like to be able to calculate the distance from the mouse position to the item in the grid. I can't seem to get this right, I've tried a few different solutions, like adding a loop in the mousemove handler and using requestAnimationFrame, but both solutions are very slow.
Here's my code below:
function setupCanvas(){
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
blockWidth = width/2 - 150;
blockHeight = height/2 - 100;
gridArray = [];
gridWidthArray = [];
ctx.fillRect(0,0,width,height);
//drawGrid();
drawInitGrid();
canvas.addEventListener('mousemove',onMouseMoveHandler);
}
function drawInitGrid(){
for(x = 0; x<16; x++){
for(y = 0; y<11; y++){
var gridBlock = new GridBlock((blockWidth) + x*20, (blockHeight) + y*20, blockWidth, blockHeight);
gridBlock.render(ctx);
gridArray.push(gridBlock);
//gridWidthArray.push(gridBlock.xPos)
}
}
}
function onMouseMoveHandler(e){
if(containerBounds(e)){
mouseX = e.offsetX;
mouseY = e.offsetY;
console.log(mouseX, mouseY);
//console.log(gridWidthArray);
for(var grid in gridArray){
//console.log(gridArray[grid].xPos)
}
}
}
I've also tried adding a mouseevent in the GridBlock object, but that also doesn't seem to work.
You can calculate the distance between any 2 points like this:
var dx=point2.x-point1.x;
var dy=point2.y-point1.y;
var distance=Math.sqrt(dx*dx+dy*dy);
Also in your fiddle your mouse position calculation should account for the offset position of the canvas within the window:
var BB=canvas.getBoundingClientRect();
var offsetX=BB.left;
var offsetY=BB.top;
function onMouseMoveHandler(e){
var mouseX=parseInt(e.clientX-offsetX);
var mouseY=parseInt(e.clientY-offsetY);
}
[ Finding nearest point in grid ]
Assume you have a mouse position [mx,my] and assume you have a grid with its top-left at [0,0] and with its cell size at cellWidth X cellHeight.
Then you can calculate the grid cell closest to the mouse like this:
var cellX=parseInt((mx+cellWidth/2)/cellWidth)*cellWidth;
var cellY=parseInt((my+cellHeight/2)/cellHeight)*cellHeight;
Of course, if the grid's top-left is not at [0,0], you will have to adjust for the gridss offset.

Mouse position on canvas painting

The code below paints correctly but it paints to wrong coordinates. It should paint the place where the mouse is. I was not able to discover my mistake. Thanks.
JSFIDDLE
container.mousedown(function(e) {
var parentOffset = $(this).offset();
var x = e.pageX - parentOffset.left;
var y = e.pageY - parentOffset.top;
context_temp.beginPath();
context_temp.moveTo(x, y);
started = true;
});
container.mousemove(function(e) {
var parentOffset = $(this).offset();
var x = e.pageX - parentOffset.left;
var y = e.pageY - parentOffset.top;
if (started) {
context_temp.lineTo(x, y);
context_temp.stroke();
}
});
container.mouseup(function(e) {
var parentOffset = $(this).offset();
var x = e.pageX - parentOffset.left;
var y = e.pageY - parentOffset.top;
if (started) {
container.mousemove(x, y);
started = false;
update();
}
});
You're setting your canvas width and height in CSS. That just stretches the canvas the same as it would an image.
The effect is drawing in the wrong place.
Instead you need to set your canvas dimensions on the tag itself:
<canvas width="400" height="400"></canvas>
A <canvas> has its own width and height, which not only define its physical size (unless CSS steps in), but also its logical size (the number of rows/columns of pixels on its drawing surface). When CSS changes the size, the canvas stretches to fit, but doesn't change its logical size. Basically, the pixels stretch too, so the logical and physical coordinates no longer match up.
To fix the problem, you could either do the math to match the coordinates back up, or exclusively use the canvases' own width/height to size them, or set the canvases' width and height properties after the fact to match the width and height set by CSS.

Canvas accessing coordinate of translated position of panning background image

Cannot figure this out, how to find the translated position of the background relative to the canvas. I have the characters coordinates, and I have the coordinates from a mouse click within the canvas, but can't figure out how to find the offset.
In the canvas, when I click somewhere, I get an (x,y) value from (0,0) - (650,575), the size of the window, no matter where my character is. If the character is at (2000, 1500) on the canvas, my click/touch input will always send the character up and left towards 0,0 on the background coordinate.
At first I thought I should subtract the player X position from the max width, then add an offset half the width of the screen, and do the same for the Y position, but that didn't work.
Then I tried subtracting half the width/height of the screen from the current player x,y values but that doesn't work.
Anyone point me in the right direction, it seems elementary but I can't figure it out it's been years since math class???? Thanks
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 650;
canvas.height = 575;
var WIDTH=5000; //level width
var HEIGHT=3750; //level height
ctx.translate(-WIDTH*.5,-HEIGHT*.5); //starts in center of background
Where my player begins on load:
hero.x = WIDTH*.5+325; //offset half canvas width
hero.y = HEIGHT*.5+275; //offset half canvas height
For the Background:
ctx.drawImage(bgImage, BGsrcX , BGsrcY, 1250 , 938 ,-150, -150, BGdestW, BGdestH); `//image is stretched to 5000x3750`
This is the mouse input I'm using
if(navigator.userAgent.match(/(iPhone)|(iPod)|(iPad)/i)){
document.addEventListener('touchstart', function(e) {
if(e.touches.length == 1){ // Only deal with one finger
var touch = e.touches[0]; // Get the information for finger #1
var x = touch.pageX - canvas.offsetLeft;
var y = touch.pageY - canvas.offsetTop;
//clickEvent(x,y); //call your function to manage tweets
}
},false);
}
else{
document.addEventListener('mousedown',function(e) {
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;
console.log(x+":"+y);
clickEvent(x,y); //call your function to manage tweets
},false);
}
For the keyboard input to actually pan the background:
if(16 in keysDown && 38 in keysDown && hero.y > 200) {ctx.translate(0,12); }
Don't work with half-translated and non-translated coordinates, translate your mouse click coordinates AND your canvas coordinates.
Then you can just use simple subtraction to find the offset, and to find the distance, you you use the distance formula.

Categories