JavaScript canvas drawing at wrong coordinates - javascript

I've got a canvas in HTML and when I draw on it when the browser window is at my standard size it works fine however when you start to scale the window (i.e shrink it), the drawing on the canvas doesn't appear where it's meant to.
var mousePressed = false;
var canvas = document.getElementById("canvas");
var lastX;
var lastY;
var colour = "black";
var size = 3;
function clear1() {
var context = canvas.getContext("2d");
context.clearRect(0, 0, canvas.width, canvas.height);
}
canvas.addEventListener("mousedown", (function(e) {
var xpos = e.pageX - this.offsetLeft;
var ypos = e.pageY - this.offsetTop;
mousePressed = true;
draw(xpos, ypos, false);
}));
canvas.addEventListener("mouseup", (function(e) {
mousePressed = false;
}));
canvas.addEventListener("mousemove", (function(e) {
var xpos = e.pageX - this.offsetLeft;
var ypos = e.pageY - this.offsetTop;
if (mousePressed) {
draw(xpos, ypos, true);
}
}));
function draw(x, y, isDown) {
if (isDown) {
context = canvas.getContext("2d");
context.beginPath();
context.strokeStyle = colour;
context.lineWidth = size;
context.lineJoin = "round";
context.moveTo(lastX, lastY);
context.lineTo(x, y);
context.closePath();
context.stroke();
}
lastX = x;
lastY = y;
}
#canvas {
border: 2px solid black;
background-color: white;
margin: auto;
display: block;
width: 200px;
height: 100px;
}
<canvas id="canvas" width="200" height="100">
</canvas>
<button onclick="clear1()" type="Submit1">Clear</button>
<input name="Submit" id="submitButton" type="submit" onclick="sendPayment()" value="Send Payment">

From Kaiido's comment:
Don't set canvas width/height through CSS, use it's own width and height properties directly

Related

Line drawn below the center of the Mouse in JavaScript

I am working on a simple drawing tool using JavaScript. However I have a problem with my draw() function. The line is always drawn slightly below the center of the mouse. May I please know what is my mistake here? I want the line to always be drawn at the center of the mouse as it moves. In my setPosition() function, does e.pageX and e.pageY actually maps the center of the mouse as x and y coordinates?
<!doctype html>
<html>
<head>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="controls">
<button class="clear">Clear</button> <span>Color
<input type="color" value="#ffff00" id="penColor"></span>
<span>Width
<input type="range" min="1" max="20" value="10" id="penWidth"></span>
</div>
<canvas id="canvas"></canvas>
<script>
let penColor = document.getElementById("penColor");
let penWidth = document.getElementById("penWidth");
let canvas = document.getElementById("canvas");
canvas.width = 700;
canvas.height = 700;
let context = canvas.getContext("2d");
let clearButton = document.querySelector(".clear");
let position = {
x: null,
y: null
}
let initialization = (e) => {
canvas.addEventListener("mousemove", draw);
canvas.addEventListener("mouseenter", setPosition)
canvas.addEventListener("mousemove", setPosition)
}
window.onload = initialization;
let setPosition = (e) => {
position.x = e.pageX;
position.y = e.pageY;
}
clearButton.addEventListener("click", (e) => {
let confirmation = confirm("Are you sure you want to clear the canvas?");
let result = confirmation ? true : false;
if (result) {
context.clearRect(0, 0, canvas.width, canvas.height);
}
})
let draw = (e) => {
if (e.buttons !== 1) return;
context.beginPath();
context.moveTo(position.x, position.y);
setPosition(e);
context.lineTo(position.x, position.y);
context.lineWidth = penWidth.value;
context.strokeStyle = penColor.value;
context.lineCap = "round";
context.stroke();
}
</script>
</body>
</html>
MouseEvent.pageX
The pageX read-only property of the MouseEvent interface returns the X (horizontal) coordinate (in pixels) at which the mouse was clicked, relative to the left edge of the entire document.
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX
so that is not the X position on the canvas, you have to substract the canvas position.
e.pageX - canvas.offsetLeft;
e.pageY - canvas.offsetTop;
let canvas = document.getElementById("canvas");
canvas.width = canvas.height = 200;
let context = canvas.getContext("2d");
let position = { x: null, y: null }
let setPosition = (e) => {
position.x = e.pageX - canvas.offsetLeft;
position.y = e.pageY - canvas.offsetTop;
}
let draw = (e) => {
if (e.buttons !== 1) return;
context.beginPath();
context.moveTo(position.x, position.y);
setPosition(e);
context.lineTo(position.x, position.y);
context.stroke();
}
canvas.addEventListener("mousemove", draw);
canvas.addEventListener("mouseenter", setPosition)
canvas.addEventListener("mousemove", setPosition)
canvas {
border: 1px solid black;
}
<canvas id="canvas"></canvas>

Why do my mouse's cordinates appear somewhere else than the mouse?

Im trying to make so when you click on a canvas, there comes a circle at the mouse's cordinates.
When i click the canvas the circle appears maybe 300 pixels to low.
I tried changing from pageX/Y, clientX/Y and screenX/Y.
<canvas id="canvas" class="canvas" width="300px" height="200px" style="border: 1px solid #000; Padding-bottom: 100px;">
</canvas>
<script>
window.onload = function()
{
var canvas = document.getElementById("canvas");
addEventListener("mousedown", drawCircle)
}
function drawCircle(event)
{
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
x = event.pageX
y = event.pageY
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(x, y, 50, 0, 2 * Math.PI);
ctx.stroke();
ctx.fill()
}
</script>
I expect the circle to appear at the mouse's cordinates.
You need to account for the position of the canvas itself. it looks like your code assumes the canvas is always in the top left of the screen.
There is also some issues with your canvas element. The width and height should just be numbers (no "px" required). The bottom padding is making the bottom half of the canvas inaccessible.
window.onload = function() {
var canvas = document.getElementById("canvas");
addEventListener("mousedown", drawCircle)
}
function drawCircle(event) {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var rect = canvas.getBoundingClientRect();
x = event.clientX - rect.left;
y = event.clientY - rect.top;
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(x, y, 50, 0, 2 * Math.PI, true);
ctx.stroke();
ctx.fill()
}
<canvas id="canvas" class="canvas" width="300" height="200" style="border: 1px solid #000;">
</canvas>
Possibly being offset by other elements on the page around the canvas. Try something like this to get x/y relative to canvas:
<canvas id="canvas" class="canvas"></canvas>
<script>
window.onload = function () {
var canvas = document.getElementById("canvas");
canvas.width = 300;
canvas.height = 200;
addEventListener("mousedown", drawCircle)
}
function drawCircle(event) {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var rect = canvas.getBoundingClientRect();
x = (event.clientX - rect.left)
y = (event.clientY - rect.top)
//x = event.pageX
//y = event.pageY
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(x, y, 50, 0, 2 * Math.PI);
ctx.stroke();
ctx.fill()
}
</script>
https://jsfiddle.net/1z5modk2/1/
Instead of using pageX and pageY, use offsetX and offsetY
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetX

Drawing on Canvas with image

I have a video element playing a mp4 video.
When clicking the screenshot button, it draws the current video frame on the canvas. I would then like to draw on top of the canvas.
My problem is that I can't get the cursor to align with the drawing coordinates. I have a suspicion that this is because of the way I'm setting the height and the width of the canvas to be able to draw the video frame correctly.
Minimal repro:
https://codepen.io/stiba/pen/KEBRdy
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var video = document.getElementById('vid');
canvas.addEventListener('mousedown', mouseDown);
canvas.addEventListener('mouseup', mouseUp);
canvas.addEventListener('mousemove', mouseMove);
var isDrawing = false;
var prevX = 0;
var prevY = 0;
var currX = 0;
var currY = 0;
function buttonClick() {
console.log('Clicked!');
var height = video.videoHeight;
var width = video.videoWidth;
canvas.width = width;
canvas.height = height;
ctx.fillRect(0, 0, width, height);
ctx.drawImage(video, 0, 0, width, height);
}
function setCoordinates(e) {
prevX = currX;
prevY = currY;
var boundingClientRect = e.target.getBoundingClientRect();
var left = boundingClientRect.left;
var top = boundingClientRect.top;
currX = e.clientX - left;
currY = e.clientY - top;
}
function mouseDown(e) {
setCoordinates(e);
isDrawing = true;
}
function mouseUp(e) {
isDrawing = false;
}
function mouseMove(e) {
if (!isDrawing) {
return;
}
setCoordinates(e);
draw();
}
function draw() {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = "red";
ctx.lineWidth = 2;
ctx.stroke();
ctx.closePath();
}
Removing the css styles from your canvas will fix the issue. If you need your canvas to be larger use canvas.width and canvas.height properties. Using css to control your canvas size will only increase the html element size but not the drawing surface(pixels).
css to be removed:
flex: 1;
width: 100%;

Canvas doesn't draw IE 11

I have a canvas code to draw a signature. The code works perfectly fine with chrome and Firefox but does not draw at all on IE 11.
My canvas is:
<canvas id="signitureCanvas" style="border: 3px solid #000; cursor:crosshair; background-color:white;"></canvas>
My code is as below:
var canvas = document.getElementById('signitureCanvas');
var ctx = canvas.getContext('2d');
var canvasWidth = 200;
var canvasLength = 120;
canvas.width = canvasWidth;
canvas.height = canvasLength;
var canvasx = $(canvas).offset().left;
var canvasy = $(canvas).offset().top;
var last_mousex = last_mousey = 0;
var mousex = mousey = 0;
var mousedown = false;
var tooltype = 'draw';
//Mousedown
$(canvas).on('mousedown', function (e) {
last_mousex = mousex = parseInt(e.clientX - canvasx);
last_mousey = mousey = parseInt(e.clientY - canvasy);
mousedown = true;
});
//Mouseup
$(canvas).on('mouseup', function (e) {
mousedown = false;
});
//Mousemove
$(canvas).on('mousemove', function (e) {
mousex = parseInt(e.clientX - canvasx);
mousey = parseInt(e.clientY - canvasy);
if (mousedown) {
ctx.beginPath();
if (tooltype == 'draw') {
ctx.globalCompositeOperation = 'source-over';
ctx.strokeStyle = 'black';
ctx.lineWidth = 3;
} else {
ctx.globalCompositeOperation = 'destination-out';
ctx.lineWidth = 10;
}
ctx.moveTo(last_mousex, last_mousey);
ctx.lineTo(mousex, mousey);
ctx.lineJoin = ctx.lineCap = 'round';
ctx.stroke();
}
last_mousex = mousex;
last_mousey = mousey;
});
function ClearCanvas() {
var canvas = document.getElementById('signitureCanvas');
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
Is IE 11 in particular having problems?
Edit:
I figured out the problem is with my Iframe:
When height and width are set to 300 everything works fine:
<embed id="fred" type="application/pdf" style="border:1px solid #666CCC" title="PDF in an i-Frame" src="#Model.FilePath" frameborder="1" scrolling="yes" height="300" width="300" />
When I set it to 1000, it won't work:
<embed id="fred" type="application/pdf" style="border:1px solid #666CCC" title="PDF in an i-Frame" src="#Model.FilePath" frameborder="1" scrolling="yes" height="1000" width="1000" />
I believe it's something with the offset but I can't figure how to fix it.
any help?
For further knowledge to whom might ask:
I have found this piece of code that works on all browsers, layerX and layerY are different for firefox browsers:
var canvas = document.getElementById('signitureCanvas');
var ctx = canvas.getContext('2d');
var canvasWidth = 200;
var canvasLength = 120;
canvas.width = canvasWidth;
canvas.height = canvasLength;
var x = 0;
var y = 0;
function tool_pencil() {
var tool = this;
this.started = false;
// This is called when you start holding down the mouse button
// This starts the pencil drawing
this.mousedown = function (ev) {
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.lineWidth = 3;
ctx.lineJoin = ctx.lineCap = 'round';
ctx.moveTo(x, y);
tool.started = true;
};
// This function is called every time you move the mouse. Obviously, it only
// draws if the tool.started state is set to true (when you are holding down
// the mouse button)
this.mousemove = function (ev) {
if (tool.started) {
ctx.lineTo(x, y);
ctx.stroke();
}
};
// This is called when you release the mouse button
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
}
};
}
// The general-purpose event handler. This function just determines
// the mouse position relative to the <canvas> element
function ev_canvas(ev) {
// Firefox
if (ev.offsetX || ev.offsetX == 0) {
x = ev.offsetX;
y = ev.offsetY;
// Opera
} else if (ev.layerX || ev.layerX == 0) {
x = ev.layerX;
y = ev.layerX;
}
// Call the event handler of the tool
var func = tool[ev.type];
if (func) {
func(ev);
}
}
function ClearCanvas() {
var canvas = document.getElementById('signitureCanvas');
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
}

How to fade out an item in canvas

I have a full screen canvas in my page. There are also some dive elements over this canvas. there is an circle element in the canvas that moves with the cursor everywhere in the page. However when the cursor arrives to the div element over the canvas, the circle shape stays in the last place it was on the canvas before arriving to the div.
DEMO: JSFIDDLE
Is ther any way that I can fade-out the circle shape when the cursor is over the div element and fade it in when it backs to the canvas?
Also is there any other effect rather than fading out? like making it small and then fade-out...
Here is the bit of code related to the circle:
function writeMessage(canvas, message, x, y) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
var pattern = context.createPattern(imageResized, 'no-repeat');//Use imageResized, not imageObj.
context.fillStyle = pattern;
context.fill();
context.fillStyle = 'black';
context.beginPath();
context.arc(x, y, 60, 0, 2 * Math.PI);
}
Well, you can always create your own fade function that gets called on mouseout (or mouseleave) event. Here's one I quickly built for you:
function fadeOut(canvas, message, x, y, amount) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
var pattern = context.createPattern(imageResized, 'no-repeat');
context.fillStyle = pattern;
context.fill();
context.font = '28pt Calibri';
context.fillStyle = 'black';
context.beginPath();
context.arc(x, y, amount, 0, 2 * Math.PI);
if (amount > 0) {
setTimeout(function(){
fadeOut(canvas, message, x, y, --amount);
}, 2);
}
else {
context.clearRect(0, 0, canvas.width, canvas.height);
}
}
See it in action here: http://jsfiddle.net/2p9dn8ed/42/
Or in the snippet:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = 0;
var y = 0;
var width = window.innerWidth;
var height = window.innerHeight;
var imageObj = new Image();
console.log(window.innerWidth + "----" + window.innerHeight);
//Create another canvas to darw a resized image to.
var imageResized = document.createElement('canvas');
imageResized.width = width;
imageResized.height = height;
//Wait for the original image to low to draw the resize.
imageObj.onload = function() {
//Find hoe mauch to scale the image up to cover.
var scaleX = width / imageObj.width;
var scaleY = height / imageObj.height;
var scaleMax = Math.max(scaleX, scaleY);
var ctx = imageResized.getContext('2d');
ctx.scale(scaleMax, scaleMax);
ctx.drawImage(imageObj, 0, 0);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
function writeMessage(canvas, message, x, y) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
var pattern = context.createPattern(imageResized, 'no-repeat');//Use imageResized, not imageObj.
context.fillStyle = pattern;
context.fill();
context.font = '28pt Calibri';
context.fillStyle = 'black';
//context.fillText(message, x, y);
context.beginPath();
context.arc(x, y, 60, 0, 2 * Math.PI);
//context.stroke();
//
}
function fadeOut(canvas, message, x, y, amount) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
var pattern = context.createPattern(imageResized, 'no-repeat');//Use imageResized, not imageObj.
context.fillStyle = pattern;
context.fill();
context.font = '28pt Calibri';
context.fillStyle = 'black';
//context.fillText(message, x, y);
context.beginPath();
context.arc(x, y, amount, 0, 2 * Math.PI);
//context.stroke();
//
if (amount > 0) {
setTimeout(function(){
fadeOut(canvas, message, x, y, --amount);
}, 2);
}
else {
context.clearRect(0, 0, canvas.width, canvas.height);
}
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
canvas.addEventListener('mousemove', function (evt) {
var mousePos = getMousePos(canvas, evt);
var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
writeMessage(canvas, message, mousePos.x, mousePos.y);
}, false);
canvas.addEventListener('mouseout', function(evt){
var mousePos = getMousePos(canvas, evt);
var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
console.log(1);
fadeOut(canvas, message, mousePos.x, mousePos.y, 60);
});
// Get the canvas element form the page
var canvas = document.getElementById("myCanvas");
/* Rresize the canvas to occupy the full page,
by getting the widow width and height and setting it to canvas*/
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
anvas, img {
display:block;
margin:1em auto;
border:1px solid black;
}
canvas {
background:url('../img/spiral_galaxy-1920x1080.jpg');
background-size: cover;
position: absolute;
left: 0; top: 0;
width: 100%; height: 100%;
z-index:-1;
}
div{
width:200px;
height:200px;
background:rgba(0,0,0,0.5);
position: fixed;
top: 20%;
left: 20%;
}
<canvas id="myCanvas" width="578" height="400"></canvas>
<div><h1>TEXT</h1></div>

Categories