Canvas mouse event and focus - javascript

I have 2 circles and one line. The circles have a move option (u can press them and move). Mousemove event have a variable distance to calculate the distance between mouse and the circle.
The problem is, that when you move one circle to another close enough, they will join. How to avoid that? Is there an option to make some focus or something like that? Any ideas how to fix that (is it possible?)
My code:
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d'),
radius = 12,
p = null,
start = false;
point = {
p1: { x:100, y:250 },
p2: { x:400, y:100 }
}
function init() {
return setInterval(draw, 10);
}
canvas.addEventListener('mousedown', function(e) {
start = true;
});
canvas.addEventListener('mouseup', function(e) {
start = false;
});
canvas.addEventListener('mousemove', function(e) {
for (p in point) {
if(start) {
var
mouseX = e.clientX -10,
mouseY = e.clientY -10,
distance = Math.sqrt(Math.pow(mouseX - point[p].x, 2) + Math.pow(mouseY - point[p].y, 2));
if (distance -10<= radius) {
point[p].x = e.clientX -10 ;
point[p].y = e.clientY -10 ;
}
}
}
});
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.moveTo(point.p1.x,point.p1.y);
context.lineTo(point.p2.x,point.p2.y);
context.closePath();
context.fillStyle = '#8ED6FF';
context.fill();
context.stroke();
for (p in point) {
context.beginPath();
context.arc(point[p].x,point[p].y,radius,0,2*Math.PI);
context.fillStyle = 'red';
context.fill();
context.stroke();
}
context.closePath();
}
init();
​

Try to save which point was pressed in your mousedown event.
Like that: http://jsfiddle.net/cs5Sg/9/
In addition, it will not calculate the distance to each point every time the mouse moves so it will be faster.

Related

Javascript - Need to get x and y coordinates only from the stroke of a SVG path?

I have a canvas in which, is drawn an element svg (example a circle), the user is responsible for drawing with the mouse through this figure, I save the dots x and y drawn by the user in an array, but I dont know how to get the dots only from svg stroke.
My problem is:
Using isPointInStroke() I can see if the point is in the stroke but If I don't have the total points array of the stroke, it's impossible to know if the user has drawn 100% of the SVG figure. In the previous way if the user draws half of the drawing but correctly, it would give me 100% success.
function init() {
canvas = document.getElementById('can');
ctx = canvas.getContext("2d");
w = canvas.width;
h = canvas.height;
var svgPathCirculo=" M125,200a75,75 0 1,0 150,0a75,75 0 1,0 -150,0";
var circulo = new Path2D(svgPathCirculo);
ctx.lineWidth = 5;
ctx.setLineDash([5, 15]);
ctx.stroke(circulo);
// Just example to check if it works
if(ctx.isPointInStroke(circulo, 125, 200)){
ctx.arc(200,200,3,0,2*Math.PI);
ctx.fill();
};
canvas.addEventListener("mousemove", function (e) {
findxy('move', e)
}, false);
canvas.addEventListener("mousedown", function (e) {
findxy('down', e)
}, false);
canvas.addEventListener("mouseup", function (e) {
findxy('up', e)
}, false);
canvas.addEventListener("mouseout", function (e) {
findxy('out', e)
}, false);
}
I use the canvas to draw on it and svg to display predefined shapes for the user to follow as a template while drawing (such as drawing booklets for young children).
function draw() {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = x;
ctx.lineWidth = y;
ctx.stroke();
ctx.closePath();
}
function findxy(res, e) {
if (res == 'down') {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
if(!arrayCoordenadas.includes({x:currX,y:currY})){
arrayCoordenadas.push({x:currX,y:currY});
}
flag = true;
dot_flag = true;
if (dot_flag) {
ctx.beginPath();
ctx.fillStyle = x;
ctx.fillRect(currX, currY, 2, 2);
ctx.closePath();
dot_flag = false;
}
}
if (res == 'up' || res == "out") {
flag = false;
}
if (res == 'move') {
if (flag) {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
if(!arrayCoordenadas.includes({x:currX,y:currY})){
arrayCoordenadas.push({x:currX,y:currY});
}
draw();
}
}
}
I need to know each of the x and y coordinates of the svg stroke path.
Example: Example of what I mean
I've added a function to detect the mouse position in the canvas and now currX became curr.x ... etc
If you are using Path2Dthis is how you detect if a point {x,y} is in the stroke:
ctx.isPointInStroke(the_path, x, y)
Next comes my code. The user can draw only inside the stroke.
Now the code is working but I don't think you may know if the user has drawn 100% of the SVG figure. You may push the points inside the array of points and calculate the length of the path, and compare it with the length of the circle, but I don't think this would do.
let prev = {},
curr = {};
let flag = false;
let circulo;
function init() {
canvas = document.getElementById("can");
ctx = canvas.getContext("2d");
w = canvas.width = 400;
h = canvas.height = 400;
var svgPathCirculo = "M125,200a75,75 0 1,0 150,0a75,75 0 1,0 -150,0";
circulo = new Path2D(svgPathCirculo);
ctx.lineWidth =10;
ctx.setLineDash([5, 15]);
ctx.stroke(circulo);
canvas.addEventListener("mousemove", move, false);
canvas.addEventListener("mousedown", down, false);
canvas.addEventListener("mouseup", up, false);
canvas.addEventListener("mouseout", up, false);
}
function draw(prev, curr, trazado) {
ctx.setLineDash([]); //unset linedash
ctx.lineCap = "round";
ctx.strokeStyle = "gold"
ctx.lineWidth =5;
if (
ctx.isPointInStroke(trazado, curr.x, curr.y) &&
ctx.isPointInStroke(trazado, prev.x, prev.y)
) {
ctx.beginPath();
ctx.moveTo(prev.x, prev.y);
ctx.lineTo(curr.x, curr.y);
ctx.stroke();
}
}
function down(e) {
prev = oMousePos(canvas, e);
curr = oMousePos(canvas, e);
flag = true;
}
function up(e) {
flag = false;
}
function move(e) {
if (flag) {
curr = oMousePos(canvas, e);
draw(prev, curr, circulo);
prev = { x: curr.x, y: curr.y };
}
}
function oMousePos(canvas, evt) {
var ClientRect = canvas.getBoundingClientRect();
return {
//objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
};
}
init();
canvas{border:1px solid}
<canvas id="can"></canvas>

Drawing a parallelogram HTML5 Canvas

I am new to Javascript and Canvas of HTML5. I have to complete a project where I have to draw a parallelogram with three mouse clicks.
Click 1: Starts the first line of the parallelogram.
Click 2: Ends of the first line.
Click 3: When the user drags the mouse up or down from the second click point, a line should be drawn from the second click point along the mouse move and at the same time a third line should be drawn from the first click point parallel to the second line.Upon the third click on the canvas the parallelogram should be complete i.e, a line should be drawn from the second line to the third line.
I am stuck on Click 3. While I conceptually understand how this needs to be done...for the last one week...I could not do much headway. The following is my code:
var canvas, context;
var dragging = false;
var dragStartLocation;
var dragStopLocation;
var dragThirdLocation;
var beginFourthLine;
var snapshot;
var pointsNum;
//Get mouse click coordinates
function getCanvasCoordinates(event) {
var x = event.clientX - canvas.getBoundingClientRect().left;
var y = event.clientY - canvas.getBoundingClientRect().top;
return {
x: x,
y: y
};
}
//save the canvas original state
function takeSnapShot() {
snapshot = context.getImageData(0, 0, canvas.width, canvas.height);
}
//restore the canvas original state
function restoreSnapShot() {
context.putImageData(snapshot, 0, 0);
}
//draw a point on mouse click
function drawPoint(position) {
context.beginPath();
context.lineWidth = 3;
context.strokeStyle = '#f4d03f';
context.arc(position.x, position.y, 5.5, 0, Math.PI * 2, false);
context.stroke();
}
//draw a line on mouse move
function drawLine(position) {
context.beginPath();
context.moveTo(dragStartLocation.x, dragStartLocation.y);
context.lineTo(position.x, position.y);
context.stroke();
}
//start the event with first mouse click
function dragStart(event) {
dragging = true;
dragStartLocation = getCanvasCoordinates(event);
drawPoint(dragStartLocation);
console.log(dragStartLocation.x, dragStartLocation.y);
takeSnapShot();
}
//draw a line along with the mouse move from the first click
function drag(event) {
var position;
if (dragging === true) {
restoreSnapShot();
position = getCanvasCoordinates(event);
drawLine(position);
}
}
//draw the third and fourth coordinates - this is where I am stuck
function drawThirdCoord(event) {
dragging = true;
var beginFourthLine = dragStopLocation.x - dragStartLocation.x;
restoreSnapShot();
dragThirdLocation = getCanvasCoordinates(event);
drawLine(event);
drawLine(beginFourthLine);
}
//stop the mouse movement and drawing line.
function dragStop(event) {
dragging = false;
restoreSnapShot();
var position = getCanvasCoordinates(event);
dragStopLocation = position;
drawLine(position);
drawPoint(position);
console.log(dragStopLocation.x, dragStopLocation.y);
drawThirdCoord(event);
}
function init() {
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
context.strokeStyle = 'green';
context.lineWidth = 6;
context.lineCap = "round";
canvas.addEventListener('mousedown', dragStart, false);
canvas.addEventListener('mousemove', drag, false);
canvas.addEventListener('mouseup', dragStop, false);
}
window.addEventListener('load', init, false);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<canvas id="canvas" width="800" height="600" style="border:solid 1px;margin:0;padding:0;"></canvas>
<p id="status"> | </p>
I'm not sure how the mouse drag should work, so I tried to keep the code as close to the question as possible. So you need to drag the first line then just click to end the shape.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>parallelogram</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<canvas id="canvas" width="800" height="600" style="border:solid 1px;margin:0;padding:0;"></canvas>
<p id="status"> | </p>
</body>
</html>
<script type="text/javascript">
var canvas, context;
var dragging = false;
var startLocation;
var dragStartLocation;
var dragStopLocation;
var dragThirdLocation;
var snapshot;
var pointsNum = 0;
var d = {x:0, y:0};
//Get mouse click coordinates
function getCanvasCoordinates(event) {
var x = event.clientX - canvas.getBoundingClientRect().left;
var y = event.clientY - canvas.getBoundingClientRect().top;
return {x: x, y: y};
}
//save the canvas original state
function takeSnapShot() {
snapshot = context.getImageData(0,0,canvas.width, canvas.height);
}
//restore the canvas original state
function restoreSnapShot() {
context.putImageData(snapshot,0,0);
}
//draw a point on mouse click
function drawPoint(position) {
context.beginPath();
context.arc(position.x, position.y, 5.5, 0, Math.PI * 2, false);
context.stroke();
}
//draw a line on mouse move
function drawLine(start, end) {
context.beginPath();
context.moveTo(start.x, start.y);
context.lineTo(end.x, end.y);
context.stroke();
}
//start the event with first mouse click
function dragStart(event) {
dragging = true;
dragStartLocation = getCanvasCoordinates(event);
drawPoint(dragStartLocation);
pointsNum++;
takeSnapShot();
if (pointsNum == 1) startLocation = dragStartLocation;
}
//draw a line along with the mouse move from the first click
function drag(event) {
var position;
if (snapshot && pointsNum && pointsNum < 3) {
restoreSnapShot();
position = getCanvasCoordinates(event);
drawLine(dragStartLocation, position);
drawPoint(position);
if (pointsNum == 2) drawFourthCoord(position)
}
}
//stop the mouse movement and drawing line.
function dragStop(event) {
dragging = false;
restoreSnapShot();
var position = getCanvasCoordinates(event);
dragStopLocation = position;
drawPoint(dragStopLocation);
pointsNum++;
drawLine(dragStartLocation, dragStopLocation);
takeSnapShot();
d = {
x: dragStartLocation.x - dragStopLocation.x,
y: dragStartLocation.y - dragStopLocation.y
};
dragStartLocation = position;
if (pointsNum > 3) pointsNum =0;
}
//draw the fourth coordinate
function drawFourthCoord(position) {
var p = {
x: position.x + d.x,
y: position.y + d.y
};
drawLine(position, p);
drawPoint(p);
drawLine(startLocation, p);
}
function init() {
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
context.lineCap = "round";
context.lineWidth = 3;
context.strokeStyle = '#f4d03f';
canvas.addEventListener('mousedown', dragStart, false);
canvas.addEventListener('mousemove', drag, false);
canvas.addEventListener('mouseup', dragStop, false);
}
window.addEventListener('load', init, false);
</script>

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

Animation stops after mousemove (canvas)

I created a script that makes a canvas circle follow the mouse when is X is bigger.However as you can see it only works during the mouse move. when the mouse stops I couldn't find a way to make the circle move. Plus, did i use the correct logic for making this code?
Heres a snippet:
canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
var PI = Math.PI;
window.requestAnimFrame = (function(){
return window.requestAnimationFrame || // La forme standardisée
window.webkitRequestAnimationFrame || // Pour Chrome et Safari
window.mozRequestAnimationFrame || // Pour Firefox
window.oRequestAnimationFrame || // Pour Opera
window.msRequestAnimationFrame || // Pour Internet Explorer
function(callback){ // Pour les élèves du dernier rang
window.setTimeout(callback, 1000 / 60);
};
})();
function pos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
function elm_fixe() {
ctx.fillStyle = "rgba(050, 155, 255, 1)";
ctx.fillRect(0, 0, 30, 30, 1);
for (var x = 0, y = 0, alpha = 1; alpha >= 0; alpha -= 0.1, x += 40) {
ctx.fillStyle = "rgba(050, 155, 255, " + alpha + ")";
ctx.fillRect(x, 0, 30, 30);
}
}
function cercle(x, y) {
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(x, y, 30, 0, PI * 2, true);
ctx.fill();
}
var x = 250,
y = 250;
function bouger(e) {
console.log(e.clientX)
if ( pos(canvas, e).x > x) {
x += 1;
};
}
function draw(e) {
ctx.clearRect(0, 0, 800, 500);
bouger(e);
cercle(x, y);
elm_fixe();
}
/* window.requestAnimFrame(function() {
canvas.addEventListener('mousemove', function(e) {
window.requestAnimFrame(function() { draw(e) });
});
}
);
*/
window.addEventListener('mousemove', function(e) {
draw(e);
});
<canvas height="500" width="800" id="canvas"></canvas>
First of all, when experimenting with this it's important to focus on getting the task at hand done, so I removed your fixed drawn elements, as that's easy.
The main issue you are having is that you only update onmousemove, which could get in your way. The best thing to do is simply to store the mouse coordinates in a separate object, here I have done it as such:
var mouse = {x: 0, y: 0};
After that, simply update the coordinates when the mousemove event fires. Now we remember the position, which means in the future you could actually animate this circle from point to point as it does not rely on the event to actually know the values.
A polyfill for requestAnimationFrame is actually no longer necessary, almost every browser supports it except some older ones.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var mouse = {x: 0, y: 0};
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function circle() {
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(mouse.x, mouse.y, 30, 0, Math.PI * 2, true);
ctx.fill();
}
function draw(e) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
circle();
window.requestAnimationFrame(draw);
}
document.addEventListener('mousemove', function(e) {
mouse.x = e.pageX > mouse.x ? e.pageX : mouse.x;
mouse.y = e.pageY > mouse.y ? e.pageY : mouse.y;
});
window.requestAnimationFrame(draw);
html, body { height: 100%; }
body { overflow: hidden; padding: 0; margin: 0;}
<canvas id="canvas"></canvas>
It might be better to rename some functions, just for future obviousness. I have also learned the hard way, a long time ago, that you should keep your function names in English, mostly because programming happens to be based on English. This way every user on this site can decipher what a function might do, and future developers will be able to debug your code without knowing french. For example, I would rename circle to something like drawCircleAtMousePosition - its a mouthful, but nobody can confuse what this function does.
Another advantage of using a stored variable is that you can do your pos (which is a really bad name for a function - maybe localiseCoordinatesTo(canvas)) right in the onmousemove event, so you never have to think about this at a later point.
Update: Animating
Here is an implementation that uses a very simple linear interpolation to animate the circle from place to place:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// We will need a from value, a to value, and store a time;
var mouse = {from: {x: 0, y: 0}, to: {x: 0, y: 0}, time: Date.now()};
// As well as a duration
var duration = 1000;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function position(){
// This will calculate the position
var time = Date.now(), progress;
if(time > mouse.time + duration)
return mouse.to;
else
progress = (time - mouse.time) / duration;
return {
x: mouse.from.x + (mouse.to.x - mouse.from.x) * progress,
y: mouse.from.y + (mouse.to.y - mouse.from.y) * progress
}
}
function circle() {
ctx.fillStyle = "red";
ctx.beginPath();
var pos = position();
ctx.arc(pos.x, pos.y, 30, 0, Math.PI * 2, true);
ctx.fill();
}
function draw(e) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
circle();
window.requestAnimationFrame(draw);
}
document.addEventListener('mousemove', function(e) {
// Update FROM to the current position
mouse.from = position();
// Reassign the to values
mouse.to.x = e.pageX > mouse.to.x ? e.pageX : mouse.to.x;
mouse.to.y = e.pageY > mouse.to.y ? e.pageY : mouse.to.y;
// Update the animation start time.
mouse.time = Date.now();
});
window.requestAnimationFrame(draw);
html, body { height: 100%; }
body { overflow: hidden; padding: 0; margin: 0;}
<canvas id="canvas"></canvas>

How can I stop HTML5 Canvas Ghosting?

I made a small program that:
changes the mouse cursor inside the canvas to a black square
gives the black square a nice trail that fades away over time (the point of the program)
Here's the code:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
canvas.style.cursor = 'none'; // remove regular cursor inside canvas
function getMousePos(canvas, e) {
var rect = canvas.getBoundingClientRect();
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}
function fadeCanvas() {
ctx.save();
ctx.globalAlpha = 0.1; // the opacity (i.e. fade) being applied to the canvas on each function re-run
ctx.fillStyle = "#FFF";
ctx.fillRect(0, 0, canvas.width, canvas.height); // area being faded (whole canvas)
ctx.restore();
requestAnimationFrame(fadeCanvas); // animate at 60 fps
}
fadeCanvas();
function draw(e) {
var pos = getMousePos(canvas, e);
ctx.fillStyle = "black";
ctx.fillRect(pos.x, pos.y, 8, 8); // the new cursor
}
addEventListener('mousemove', draw, false);
Here's a live example: https://jsfiddle.net/L6j71crw/2/
Problem
However the trail does not fade away completely, and leaves a ghosting trail.
Q: How can I remove the ghosting trail?
I have tried using clearRect() in different ways, but it just clears the entire animation leaving nothing to display. At best it just removes the trail and only fades the square cursor alone, but it still doesn't make the cursor completely transparent when the fading process is completed. I have tried finding posts about it, but I found nothing that gave a definitive answer and—most importantly—no posts with a working example.
Any ideas?
Try having a list of positions, this won't leave a ghost trail!
my code:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var Positions = [];
var maxlength = 20;
canvas.style.cursor = 'none'; // remove regular cursor inside canvas
var V2 = function(x, y){this.x = x; this.y = y;};
function getMousePos(canvas, e) {
// ctx.clearRect(0, 0, canvas.width, canvas.height);
var rect = canvas.getBoundingClientRect();
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}
function fadeCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(var e = 0; e != Positions.length; e++)
{
ctx.fillStyle = ctx.fillStyle = "rgba(0, 0, 0, " + 1 / e + ")";
ctx.fillRect(Positions[e].x, Positions[e].y, 8, 8);
}
if(Positions.length > 1)
Positions.pop()
//ctx.save();
//ctx.globalAlpha = 0.5; // the opacity (i.e. fade) being applied to the canvas on each function re-run
//ctx.fillStyle = "#fff";
//ctx.fillRect(0, 0, canvas.width, canvas.height); // area being faded (whole canvas)
//ctx.restore();
requestAnimationFrame(fadeCanvas); // animate at 60 fps
}
fadeCanvas();
function draw(e) {
var pos = getMousePos(canvas, e);
Positions.unshift(new V2(pos.x, pos.y));
if(Positions.length > maxlength)
Positions.pop()
//ctx.fillStyle = "black";
//ctx.fillRect(pos.x, pos.y, 8, 8); // the new cursor
}
addEventListener('mousemove', draw, false);
JSFiddle: https://jsfiddle.net/L6j71crw/9/
Edit: made the cursor constant.

Categories

Resources