Mouse cursor doesn't match with canvas - javascript

I have question: when I'm drawing a line in canvas, it seems the mouse position doesn't match with the canvas position, so whenever I draw, there is some distance between my cursor and the drawing line .. please help me with this problem, here is my code :
$(document).ready(function(){
context = document.getElementById('canvasInAPerfectWorld').getContext("2d");
$('#canvasInAPerfectWorld').mousedown(function(e){
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;
paint = true;
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
redraw();
});
$('#canvasInAPerfectWorld').mousemove(function(e){
if(paint){
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
redraw();
}
});
$('#canvasInAPerfectWorld').mouseup(function(e){
paint = false;
});
$('#canvasInAPerfectWorld').mouseleave(function(e){
paint = false;
});
});
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
function addClick(x, y, dragging)
{
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
function clear_canvas(){
//alert('masuk claear');
context.clearRect(0,0,context.canvas.width,context.canvas.height);
}
function redraw(){
context.strokeStyle = "#df4b26";
context.lineJoin = "round";
context.lineWidth = 5;
for(var i=0; i < clickX.length; i++) {
context.beginPath();
if(clickDrag[i] && i){
context.moveTo(clickX[i-1], clickY[i-1]);
}else{
context.moveTo(clickX[i]-1, clickY[i]);
}
context.lineTo(clickX[i], clickY[i]);
context.closePath();
context.stroke();
}
}

Inside your mouse event handlers, this refers to the window object and your this.offsetLeft is undefined.
You can use getBoundingClientRect to get the bounds of your canvas element:
// get a reference to your canvas element at the start of your app
var canvas=document.getElementById('canvasInAPerfectWorld');
// example mousedown handler
// get the current canvas offsets using getBoundingClientRect
var BB=canvas.getBoundingClientRect();
var offsetX=BB.left;
var offsetY=BB.top;
// calculate the current mouse position relative to the canvas
// using e.client and the offsets calculated above
var mouseX=parseInt(e.clientX-offsetX);
var mouseY=parseInt(e.clientY-offsetY);
If you canvas does not reposition relative to the viewport, you can get the offsets once at the start of your app so they don't need to be recalculated every time inside the mouse handler.

You could follow the solution in markE's answer (also found here).
Or you could do the following if your layout allows
Set canvas element to position relative
Use layerX and layerY to read the mouse position
This approach gives a little simpler code.
Both methods will be affected by padding and border thickness (they need to be subtracted if any is used). If you want border/padding it's better to wrap the canvas in a div and then style the div instead.
Example using relative positioned canvas
var c = document.querySelector("canvas"),
ctx = c.getContext("2d");
ctx.font = "bold 16px sans-serif";
c.onmousemove = function(e) {
var x = e.layerX,
y = e.layerY;
ctx.clearRect(0, 0, 300, 20);
ctx.fillText("x: " + x + ", y: " + y, 10, 16);
};
div {padding:20px}
canvas {background:#eee; position:relative}
<div><div><canvas></canvas></div></div>
Example using getBoundingClientRect()
var c = document.querySelector("canvas"),
ctx = c.getContext("2d");
ctx.font = "bold 16px sans-serif";
c.onmousemove = function(e) {
var rect = this.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top;
ctx.clearRect(0, 0, 300, 20);
ctx.fillText("x: " + x + ", y: " + y, 10, 16);
};
div {padding:20px}
canvas {background:#eee; position:relative}
<div><div><canvas></canvas></div></div>

Related

How to correct stroke position when drawing on canvas with image? with typescript Angular

I added an image to a canvas. I want to draw on the image, but when drawing a line, it does not appear above the mouse pointer. The canvas container has a method to verify the correct position of the mouse inside the canvas and it is correct, but the drawn line appears far below the mouse pointer. How can I make the drawing stroke, where is the mouse pointer?
public canvas() {
let x;
let y;
//we determine the location of the mouse within the canvas
$('#mycanvas').mousemove(function(e){
var rect = e.target.getBoundingClientRect();
x = e.clientX - rect.left; //x position within the element.
y = e.clientY - rect.top; //y position within the element.
console.log("X : " + x );
console.log("Y : " + y );
});
const canvas = <HTMLCanvasElement> document.querySelector("#mycanvas");
const ctx = canvas.getContext("2d");
//Agregamos imagen al canvas
const image = new Image();
image.src = 'assets/img/human.png';
image.onload = function(){
ctx.drawImage(image, 0, 0, 300, 150);
}
//paint
let painting = false;
//starting position
function startPosition(e) {
painting = true;
draw(e);
}
//final position
function finishedPosition() {
painting = false;
ctx.beginPath();
}
function draw(e) {
if(!painting) {
return;
}
ctx.lineWidth = 0.5;
ctx.lineCap = "round";
ctx.lineTo(x, y);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x, y);
}
//EventListeners
canvas.addEventListener("mousedown", startPosition);
canvas.addEventListener("mouseup", finishedPosition);
canvas.addEventListener("mousemove", draw)
}
Use this function and pass the event argument to it. to get absolute x,y on canvas element.
function getPosition(e) {
this.boundingRect = canvas.getBoundingClientRect(); // canvas = canvas element
return {
x : (e.targetTouches) ? e.targetTouches[0].pageX - this.boundingRect.x : e.offsetX,
y : (e.targetTouches) ? e.targetTouches[0].pageY - this.boundingRect.y : e.offsetY,
};
}
For optimization you can cache this.boundingRect variable, because getBoundingClientRect operation is time consuming

My mousedown function isn't drawing lines from the most recent point, but instead from the top left corner

http://codepen.io/PartTimeCoder/pen/qZJdPW?editors=0010
This is the link to my CodePen.
My HTML and the CSS are working fine. But the JavaScript isn't working the way I want it to. It should draw a line from the last point you clicked at.
The JavaScript is below -
var randomColor = function() {
return '#' + Math.random().toString(16).slice(2, 8);
}
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d")
color = randomColor();
var height = window.innerHeight
var width = window.innerWidth
canvas.width = width
canvas.height = height
var mouse = {};
var circle_count = 10;
var circles = [];
var generate = function() {
for (var i = 0; i < circle_count; i++) {
circles.push(new circle());
}
}
setInterval(generate, 7500);
canvas.addEventListener('mousedown', mousePos, false);
canvas.addEventListener('touch', mousePos, false);
function mousePos(e) {
mouse.x = e.pageX;
mouse.y = e.pageY;
}
canvas.addEventListener("mousedown", function() {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
});
You need to save last clicked position before apply new one as on example:
codepen.io/themeler/pen/XdxboL?editors=0010
Each mousedown event calls ctx.moveTo(0, 0), which positions it in the upper left.
Move this code out of your mousedown event, and it works fine:
ctx.beginPath();
ctx.moveTo(0, 0);
CodePen
Change the mouse variable to set your starting point
var mouse = {x : 0, y : 0};
and then the event handler to update the mouse variable to the latest point
canvas.addEventListener('touch', stuff);
canvas.addEventListener("mousedown", stuff);
function stuff(e) {
ctx.beginPath();
ctx.moveTo(mouse.x, mouse.y);
ctx.lineTo(e.pageX, e.pageY);
ctx.stroke();
mouse = {x: e.pageX, y: e.pageY};
}
FIDDLE

Drawing a circle in a canvas on mouseclick

I want to draw a filled (or not filled) circle in a canvas on mouseclick, but I can't get my code to work properly, I've tried pretty much everything!
This is my HTML:
<div id="images"></div>
<canvas style="margin:0;padding:0;position:relative;left:50px;top:50px;" id="imgCanvas" width="250" height="250" onclick="draw(e)"></canvas>
and my current script:
var canvas = document.getElementById("imgCanvas");
var context = canvas.getContext("2d");
function createImageOnCanvas(imageId) {
canvas.style.display = "block";
document.getElementById("images").style.overflowY = "hidden";
var img = new Image(300, 300);
img.src = document.getElementById(imageId).src;
context.drawImage(img, (0), (0)); //onload....
}
function draw(e) {
var pos = getMousePos(canvas, e);
posx = pos.x;
posy = pos.y;
context.fillStyle = "#000000";
context.arc(posx, posy, 50, 0, 2 * Math.PI);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
I think my problem is with function draw(e), even though I feel pretty confident about that part.
Here is the jsFiddle
I have forked and updated your fiddle to make a working example: http://jsfiddle.net/ankr/ds9s7/161/
Besides referencing the event incorrectly - as stated by others - you also did not begin nor end your path when drawing. Added context.beginPath() and context.fill() calls
Here's the relevant JS code
var canvas = document.getElementById("imgCanvas");
var context = canvas.getContext("2d");
function draw(e) {
var pos = getMousePos(canvas, e);
posx = pos.x;
posy = pos.y;
context.fillStyle = "#000000";
context.beginPath();
context.arc(posx, posy, 50, 0, 2*Math.PI);
context.fill();
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}

draw circle in canvas with click option

I write this javascript code :
<script>
var canvas = document.getElementById("canvas");
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);
}
$("#canvas").mousedown(function(e) {
handleMouseDown(e);
});
</script>
in this code , I detect a coordinate with mouse click .
I want draw a circle around the this coordinate that when I click on circle , do something (for ex. open google.com)
note : I do this with jquery in html 4 and with area map , but I do not have any idea in canvas .
I can't tell if you want to draw a circle, detect a mouse click in a circle or both.
Draw a circle:
var context=canvas.getContext("2d");
ctx.beginPath();
//Draw a circle around a mouse click
//ctx.arc(x-position, y-position, radius, start-angle, end-angle);
ctx.arc(mouseX, mouseY, 30, 0, 2*Math.PI);
ctx.stroke();
Detect a mouse click within a circle:
//circleX and circleY are the coordinats of the center
var y = mouseY - circleY;
var x = mouseX - circleX;
var dist = Math.sqrt(y*y + x*x);
if (dist < circleRadius) {
// Do whatever you want to do
}
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 70;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();

Drawing a rectangle on Canvas

I am trying to create a simple canvas program where the user can consistently create new shapes. This one is just a basic rectangle creator (I am hoping to expand it more to circles, lines, and maybe even other stuff). Right now though I have created something that is working in a really weird way.
<html>
<head>
<meta chartset="utf-8">
<title>Dragging a square</title>
<script type="text/javascript">
var canvas, context, startX, endX, startY, endY;
var mouseIsDown = 0;
function init() {
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.addEventListener("mousedown", mouseDown, false);
canvas.addEventListener("mousemove", mouseXY, false);
document.body.addEventListener("mouseup", mouseUp, false);
}
function mouseUp() {
mouseIsDown = 0;
//mouseXY();
}
function mouseDown() {
mouseIsDown = 1;
startX = event.clientX;
startY = event.clientY;
mouseXY();
}
function mouseXY(eve) {
if (!eve) {
var eve = event;
}
endX = event.pageX - canvas.offsetLeft;
endY = event.pageY - canvas.offsetTop;
drawSquare();
}
function drawSquare() {
// creating a square
var width = Math.abs(startX - endX);
var height = Math.abs(startY - endY);
context.beginPath();
context.rect(startX, startY, width, height);
context.fillStyle = "yellow";
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
}
</script>
</head>
<body onload="init()">
<canvas id="canvas" width="400" height="400" style="border: 1px solid black; cursor: pointer;"></canvas>
</body>
</html>
Sorry about the slightly weird formatting when I copy and pasted my code. I think the problem is my mouseXY function. What I want is the user to click somewhere on the canvas a drag the mouse to create a rectangle, when the user lets go that is the end of that operation and they can create a whole new rectangle right after. At this point the program kind of just lets me click and create a new rectangle but if I let go of the mouse button it doesn't stop, in fact I have to click again to make it stop which then creates a new rectangle. I am still very new to this and I am having a lot of trouble with this, I will continue to work on this and if I figure it out I will let the site know. Thank you and have a great day!
Well I got this to work (thanks to #Ken) but now I am trying to solve a new problem. I want to be able to put multiple rectangles on the canvas. I created a function that represents the Rectangle and then created a draw function within the rectangle function to draw out a rectangle. I created a new function called addShape() that ideally creates the rectangle object and pushes into an array called square and drawShapes() that is supposed to erase everything on the canvas and redraws everything. Here is what I have so far:
<html>
<head>
<meta chartset="utf-8">
<title>Dragging a square</title>
<script type="text/javascript">
function Rectangle(canvas, x, y, width, height,color) {
//this.context = canvas.getContext("2d");
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.draw = function() {
this.context.globalAlpha = 0.85;
this.context.beginPath();
this.context.rect(this.x, this.y, this.width, this.height);
this.context.fillStyle = this.color;
this.context.strokeStyle = "black";
this.context.lineWidth = 1;
this.context.fill();
this.context.stroke();
};
};
// hold the canvas and context variable, as well as the
// starting point of X and Y and the end ones
var canvas, context, startX, endX, startY, endY;
var mouseIsDown = 0;
// An array that holds all the squares
var squares = [];
window.onload = function() {
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.addEventListener("mousedown", mouseDown, false);
canvas.addEventListener("mousemove", mouseXY, false);
canvas.addEventListener("mouseup", mouseUp, false);
}
function mouseUp(eve) {
if (mouseIsDown !== 0) {
mouseIsDown = 0;
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
//Square(); //update on mouse-up
addShape(); // Update on mouse-up
}
}
function mouseDown(eve) {
mouseIsDown = 1;
var pos = getMousePos(canvas, eve);
startX = endX = pos.x;
startY = endY = pos.y;
// Square(); //update
addShape();
}
function mouseXY(eve) {
if (mouseIsDown !== 0) {
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
//Square();
addShape();
}
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
function addShape() {
var w = endX - startX;
var h = endY - startY;
var offsetX = (w < 0) ? w : 0;
var offsetY = (h < 0) ? h : 0;
var width = Math.abs(w);
var height = Math.abs(h);
var s = new Rectangle(startX + offsetX, startY + offsetY, width, height, "yellow");
squares.push(s);
// Update the display
drawShapes();
}
function drawShapes() {
context.clearRect(0,0,canvas.width,canvas.height);
for (var i = 0; i < squares.length; i++) {
var shape = squares[i];
shape.draw();
};
}
function clearCanvas() {
squares = [];
drawShapes();
}
</script>
</head>
<body onload="addShape()">
<canvas id="canvas" width="400" height="400" style="border: 1px solid black; cursor: pointer;"></canvas><br>
<button onclick="clearCanvas()">Clear Canvas</button>
</body>
</html>
I am pretty sure I broke the original code... thank you for any help!
You need to modify a couple of things in the code: (edit: there are many issues with this code. I went through some of them inline here, but haven't tested. If you put it in a fiddle it's easier for us to check)..
Fiddle
When mouse down occur initialize both start and end points. Call a common draw function that is not dependent on the event itself:
function mouseDown(eve) {
mouseIsDown = 1;
var pos = getMousePos(canvas, eve);
startX = endX = pos.x;
startY = endY = pos.y;
drawSquare(); //update
}
At mouse up, only register if isMouseDown is true, else this function will handle all incoming up-events (as you have attatched it to document, which is correct - window could have been used too):
function mouseUp(eve) {
if (mouseIsDown !== 0) {
mouseIsDown = 0;
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
drawSquare(); //update on mouse-up
}
}
Only draw if mouseisdown is true:
function mouseXY(eve) {
if (mouseIsDown !== 0) {
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
drawSquare();
}
}
In addition you will need to clear the previous area of the rectangle before drawing a new or else it won't show when you draw a bigger rectangle and then move the mouse back to draw a smaller one.
For simplicity you can do:
function drawSquare() {
// creating a square
var width = Math.abs(startX - endX);
var height = Math.abs(startY - endY);
context.clearRect(0, 0, context.width, context.height);
//or use fillRect if you use a bg color
context.beginPath();
context.rect(startX, startY, width, height);
context.fillStyle = "yellow";
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
}
Use this for mouse position:
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}

Categories

Resources