Remove point on double click on canvas - javascript

I have a canvas on which user can draw point on click on any part of it. As one know we must give the user the possibility to undo an action that he as done. This is where I'am stuck, how can I program the codes to allow the user to remove the point on double click on the the point he wants to remove ?
<canvas id="canvas" width="160" height="160" style="cursor:crosshair;"></canvas>
1- Codes to draw the canvas and load an image in it
var canvasOjo1 = document.getElementById('canvas'),
context1 = canvasOjo1.getContext('2d');
ojo1();
function ojo1()
{
base_image1 = new Image();
base_image1.src = 'https://www.admedicall.com.do/admedicall_v1//assets/img/patients-pictures/620236447.jpg';
base_image1.onload = function(){
context1.drawImage(base_image1, 0, 0);
}
}
2- Codes to draw the points
$("#canvas").click(function(e){
getPosition(e);
});
var pointSize = 3;
function getPosition(event){
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
drawCoordinates(x,y);
}
function drawCoordinates(x,y){
var ctx = document.getElementById("canvas").getContext("2d");
ctx.fillStyle = "#ff2626"; // Red color
ctx.beginPath();
ctx.arc(x, y, pointSize, 0, Math.PI * 2, true);
ctx.fill();
}
My fiddle :http://jsfiddle.net/xpvt214o/834918/
By hover the mouse over the image we see a cross to create the point.
How can i remove a point if i want to after create it on double click ?
Thank you in advance.

Please read first this answer how to differentiate single click event and double click event because this is a tricky thing.
For the sake of clarity I've simplified your code by removing irrelevant things.
Also please read the comments of my code.
let pointSize = 3;
var points = [];
var timeout = 300;
var clicks = 0;
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let cw = (canvas.width = 160);
let ch = (canvas.height = 160);
function getPosition(event) {
var rect = canvas.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
}
function drawCoordinates(point, r) {
ctx.fillStyle = "#ff2626"; // Red color
ctx.beginPath();
ctx.arc(point.x, point.y, r, 0, Math.PI * 2, true);
ctx.fill();
}
canvas.addEventListener("click", function(e) {
clicks++;
var m = getPosition(e);
// this point won't be added to the points array
// it's here only to mark the point on click since otherwise it will appear with a delay equal to the timeout
drawCoordinates(m, pointSize);
if (clicks == 1) {
setTimeout(function() {
if (clicks == 1) {
// on click add a new point to the points array
points.push(m);
} else { // on double click
// 1. check if point in path
for (let i = 0; i < points.length; i++) {
ctx.beginPath();
ctx.arc(points[i].x, points[i].y, pointSize, 0, Math.PI * 2, true);
if (ctx.isPointInPath(m.x, m.y)) {
points.splice(i, 1); // remove the point from the array
break;// if a point is found and removed, break the loop. No need to check any further.
}
}
//clear the canvas
ctx.clearRect(0, 0, cw, ch);
}
points.map(p => {
drawCoordinates(p, pointSize);
});
clicks = 0;
}, timeout);
}
});
body {
background: #20262E;
padding: 20px;
}
<canvas id="canvas" style="cursor:crosshair;border:1px solid white"></canvas>

Related

How to use clearRect to not draw an moving object on canvas

I have a blue circle which is rotating around the red circle and moves on canvas continuously in one direction as long as the button is pressed.
Now I want to draw with the red circle while it is moving when the button is pressed (trace of its path).
Problems:
I have tried to make changes to clearRect() but I didn't succeed. the blue circle starts to draw on the canvas while moving which I don't need.
If its not possible to do with clearRect() function, Is it possible to do this by stacking canvas layers. Please help with example
const canvas = document.getElementById('canvas1');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let positionX = 100;
let positionY = 100;
let X = 50;
let Y = 50;
let angle = 0;
let mouseButtonDown = false;
document.addEventListener('mousedown', () => mouseButtonDown = true);
document.addEventListener('mouseup', () => mouseButtonDown = false);
function circle(){
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(X, Y, 20, 0, Math.PI*2);
ctx.closePath();
ctx.fill();
}
function direction(){
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(positionX + X, positionY + Y, 10, 0, Math.PI*2);
ctx.closePath();
positionX = 35 * Math.sin(angle);
positionY = 35 * Math.cos(angle);
ctx.fill();
}
function animate(){
if (mouseButtonDown) {
X += positionX / 10;
Y += positionY / 10;
} else {
angle += 0.1;
}
ctx.clearRect(X-positionX,Y-positionY, 20, 20);
circle();
direction();
requestAnimationFrame(animate);
}
animate();
#canvas1{
position: absolute;
top:0;
left: 0;
width: 100%;
height: 100%;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas1"></canvas>
<script src="script.js"></script>
</body>
</html>
Don`t stack canvas on the page
Each canvas you add to the page increases the amount of work the GPU and page compositor needs to do to render the page.
Use a second canvas that is not on the page and do the compositing by rendering the canvas to the onpage canvas using ctx.drawImage(secondCanvas, 0, 0).
This reduces the workload for the compositor, and in many cases avoid the need to do an addition image render (composite) for the second canvas I.E. onpage can require 3 drawImages (one for each canvas and once for the result) rather than 2 (once in your code and once as the result) if you use only one onpage canvas.
Using second canvas
Create a second canvas to store the drawn red lines.
You can create a copy of a canvas using
function copyCanvas(canvas, copyContent = false) {
const can = Object.assign(document.createElement("canvas"), {
width: canvas.width, height: canvas.height
});
can.ctx = can.getContext("2d");
copyContent && can.ctx.drawImage(canvas, 0, 0);
return can;
}
When you create render functions like circle, and direction pass as an argument the 2D context eg circle(ctx) so that it is easy to direct the rendering to any canvas.
function circle(ctx){
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(X, Y, redSize, 0, Math.PI*2);
ctx.fill();
}
// the background canvas
const bgCan = copyCanvas(canvas);
circle(bgCan.ctx); // will draw to the background canvas
Updating animation
When animating is is easiest to clear the whole canvas rather than mess about clearing only rendered pixels. Clearing rendered pixels gets complicated very quickly and will end up being many times slower than clearing the whole canvas.
After you clear the canvas draw the background canvas to the main canvas
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(bgCan, 0, 0);
When the mouse button is down draw the circle to the background canvas and while it is up draw to the main canvas.
Example
Adds a function to copy a canvas. copyCanvas
Clears the main canvas, and draws the background canvas onto the main canvas.
Render functions circle and direction have argument ctx to direct rendering to any context.
When mouse is down circle is drawn to background canvas bgCan else to the main canvas.
requestAnimationFrame(animate);
const ctx = canvas1.getContext('2d');
canvas1.width = innerWidth;
canvas1.height = innerHeight;
const bgCan = copyCanvas(canvas1);
const redSize = 10, blueSize = 5; // circle sizes on pixels
const drawSpeed = 2; // when button down draw speed in pixels per frame
var X = 50, Y = 50;
var angle = 0;
var mouseButtonDown = false;
document.addEventListener('mousedown', () => mouseButtonDown = true);
document.addEventListener('mouseup', () => mouseButtonDown = false);
function copyCanvas(canvas) {
const can = Object.assign(document.createElement("canvas"), {
width: canvas.width, height: canvas.height
});
can.ctx = can.getContext("2d");
return can;
}
function circle(ctx){
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(X, Y, redSize, 0, Math.PI*2);
ctx.fill();
}
function direction(ctx){
const d = blueSize + redSize + 5;
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(d * Math.sin(angle) + X, d * Math.cos(angle) + Y, blueSize, 0, Math.PI*2);
ctx.fill();
}
function animate(){
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(bgCan, 0, 0);
if (mouseButtonDown) {
circle(bgCan.ctx);
X += Math.sin(angle) * drawSpeed;
Y += Math.cos(angle) * drawSpeed;
} else {
angle += 0.1;
circle(ctx);
}
direction(ctx);
requestAnimationFrame(animate);
}
#canvas1{
position: absolute;
top:0;
left: 0;
width: 100%;
height: 100%;
}
<canvas id="canvas1"></canvas>
BTW ctx.closePath() is like ctx.lineTo it is not the opposite to ctx.beginPath. A full arc or if you are just filling a shape you don't need to use ctx.closePath
BTW window is the default this, you don't need to include it, you dont use it to get at window.documentso why use it forwindow.innerWidth(same asinnerWidth` )
You could alter your code to keep track of the path of the red circle, with an array property, like this:
const canvas = document.getElementById('canvas1');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let mouseButtonDown = false;
document.addEventListener('mousedown', () => mouseButtonDown = true);
document.addEventListener('mouseup', () => mouseButtonDown = false);
function drawCircle({x, y, radius, color}) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI*2);
ctx.fill();
}
const red = { x: 50, y: 50, radius: 20, color: "red", path: [] };
const blue = { x: 100, y: 100, radius: 10, color: "blue", angle: 0 };
function animate(){
if (mouseButtonDown) {
red.path.push({x: red.x, y: red.y}); // store the old value
red.x += (blue.x - red.x) / 10;
red.y += (blue.y - red.y) / 10;
} else {
blue.angle += 0.1;
}
blue.x = red.x + 35 * Math.sin(blue.angle);
blue.y = red.y + 35 * Math.cos(blue.angle);
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear the whole canvas
for (const {x, y} of red.path) { // draw circle at all the previous positions
drawCircle({...red, x, y});
}
drawCircle(red);
drawCircle(blue);
requestAnimationFrame(animate);
}
animate();
Using 2 canvases also works and may perform better especially when the path of the red circle has gotten long, because the background canvas doesn't need to be cleared and redrawn. Add a 2nd canvas in your html page with the same positioning, and give them ids 'background' and 'foreground'. You can then adjust the code to draw the blue circle to the foreground and red circles to the background (or vice versa).
// Create 2 canvases, set them to full size and get the contexts
const backgroundCanvas = document.getElementById('background');
const foregroundCanvas = document.getElementById('foreground');
const background = backgroundCanvas.getContext("2d");
const foreground = foregroundCanvas.getContext("2d");
backgroundCanvas.width = innerWidth;
backgroundCanvas.height = innerHeight;
foregroundCanvas.width = innerWidth;
foregroundCanvas.height = innerHeight;
let mouseButtonDown = false;
document.addEventListener('mousedown', () => mouseButtonDown = true);
document.addEventListener('mouseup', () => mouseButtonDown = false);
// Create objects to represent the current properties of the red and blue circle
const red = { x: 50, y: 50, radius: 20, color: "red" };
const blue = { x: 100, y: 100, radius: 10, color: "blue", angle: 0};
function drawCircle(ctx, {x, y, radius, color}) {
//--- Draw a circle to the specified canvas context, ctx = foreground or background
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI*2);
ctx.closePath();
ctx.fill();
}
function animate(){
if (mouseButtonDown) {
red.x += (blue.x - red.x) / 10;
red.y += (blue.y - red.y) / 10;
} else {
blue.angle += 0.1;
}
blue.x = red.x + 35 * Math.sin(blue.angle);
blue.y = red.y + 35 * Math.cos(blue.angle);
drawCircle(background, red); // Draw the red circle in the background (without clearing the existing circles)
foreground.clearRect(0, 0, foregroundCanvas.width, foregroundCanvas.height); // Clear the foreground
drawCircle(foreground, blue); // Draw the blue circle on the foreground
requestAnimationFrame(animate);
}
animate();
Either way, it's convenient to abstract out the circle drawing code into a function or method, and to store the properties of the two circles in objects.
As #Blindman67's answer notes, there may be a performance cost of stacking 2 canvases, and if that is an issue you may want to try drawing the background offscreen then copying it to the on-screen canvas.
If you're not opposed to just building a particle class you can do it using them. In the snippet below I have a Circle class and a Particles class to creat what you are trying to achieve. I currently have the particles max at 500 but you can change it or delete that line all together if you ne er want them gone.
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let mouseButtonDown = false;
//the array holding particles
let particles = [];
//the counter is only needed it you want to slow down how fast particles are being pushed and dispolayed
let counter = 0;
document.addEventListener("mousedown", () => (mouseButtonDown = true));
document.addEventListener("mouseup", () => (mouseButtonDown = false));
//ES6 constructor class
class Circle {
//sets the basic structor of the object
constructor(r, c) {
this.x = 100;
this.y = 100;
this.x2 = 50;
this.y2 = 50;
this.r = r; //will be assigned the argument passed in through the constructor by each instance created later
this.color = c; //same as above. This allows each instance to have different parameters.
this.angle = 0;
}
//this function creates the red circle
drawRed() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
//this function creates the blue circle
drawBlue() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x + this.x2, this.y + this.y2, this.r, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
//this function is where we'll place parameter that change our object
update() {
//makes the blue circle rotate
this.x2 = 35 * Math.sin(this.angle);
this.y2 = 35 * Math.cos(this.angle);
//mouse action is same as your code
if (mouseButtonDown) {
this.x += this.x2 / 20;
this.y += this.y2 / 20;
} else {
this.angle += 0.1;
}
}
}
//When using this type of constructor class you have to create an instance of it by calling new Object. You can create as money as you want.
let blueCircle = new Circle(10, "blue"); //passing in the radius and color in to the constructor
let redCircle = new Circle(20, "red");
//another class for the particles
class Particles {
constructor() {
this.x = redCircle.x;
this.y = redCircle.y;
this.r = redCircle.r;
this.color = redCircle.color;
}
draw() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
}
//just wrapping all of the particle stuff into one function
function handleParticles() {
//while the mouse is held it will push particles
if (mouseButtonDown) {
particles.push(new Particles());
}
//this loops through the array and calls the draw() function for each particle
for (let i = 0; i < particles.length; i++) {
particles[i].draw();
}
//this keeps the array from getting too big.
if (particles.length > 500) {
particles.shift();
}
}
//wrap all functions into this one animate one and call requeatAnimationFrame
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
handleParticles();
//These must be called for each instance created of the object
blueCircle.drawBlue();
blueCircle.update();
redCircle.drawRed();
redCircle.update();
requestAnimationFrame(animate);
}
animate();
#canvas1{
position: absolute;
top:0;
left: 0;
width: 100%;
height: 100%;
}
<canvas id="canvas"></canvas>
I'd also like to add you can change the rate that the particles are drawn by adding a counter variable and then limiting the draw like counter % 10 == 0
EXAMPLE
add global variable let counter = 0;
then in the handleParticles function add this
function handleParticles() {
counter++
if (mouseButtonDown && counter % 10 == 0) {
particles.push(new Particles());
}
for (let i = 0; i < particles.length; i++) {
particles[i].draw();
}
if (particles.length > 500) {
particles.shift();
}
}

Get dimensions of a HTML5 canvas graphic (Arc), when an event occurs

My question is: how should I do the logical and mathematical part, in order to calculate the position of each arc, when an event is emitted: "click", "mouseover", etc.
Points to consider:
. Line width
. They are rounded lines.
. Arc length in percentage.
. Which element is first, ej: z-index position.
My source code
Thank you for your time.
There is a convenient isPointInStroke() method, just for that.
To take the z-index into consideration, just check in the reverse order you drew:
const ctx = canvas.getContext('2d');
const centerX = canvas.width/2;
const centerY = canvas.height/2;
const rad = Math.min(centerX, centerY) * 0.75;
const pathes = [
{
a: Math.PI/4,
color: 'white'
},
{
a: Math.PI/1.5,
color: 'cyan'
},
{
a: Math.PI,
color: 'violet'
},
{
a: Math.PI*2,
color: 'gray'
}
];
pathes.forEach(obj => {
const p = new Path2D();
p.arc(centerX, centerY, rad, -Math.PI/2, obj.a-Math.PI/2);
obj.path = p;
});
ctx.lineWidth = 12;
ctx.lineCap = 'round';
function draw() {
ctx.clearRect(0,0,canvas.width,canvas.height);
// since we sorted first => higher z-index
for(let i = pathes.length-1; i>=0; i--) {
let p = pathes[i];
ctx.strokeStyle = p.hovered ? 'green' : p.color;
ctx.stroke(p.path);
};
}
function checkHovering(evt) {
const rect = canvas.getBoundingClientRect();
const x = evt.clientX - rect.left;
const y = evt.clientY - rect.top;
let found = false;
pathes.forEach(obj => {
if(found) {
obj.hovered = false;
}
else {
found = obj.hovered = ctx.isPointInStroke(obj.path, x, y);
}
});
draw();
}
draw();
canvas.onmousemove = canvas.onclick = checkHovering;
canvas{background: lightgray}
<canvas id="canvas"></canvas>
And if you need IE support, this polyfill should do.
It's much better to "remember" the objects you drew, rather than drawing them and trying to gather what they are from what you drew. So, for example, you could store the render information: (I don't know typescript)
let curves = [{start: 30, length: 40, color: "white"}/*...*/];
Then, render it:
ctx.fillStyle = curve.color;
ctx.arc(CENTER_X, CENTER_Y, RADIUS, percentToRadians(curve.start), percentToRadians(curve.start + curve.length));
Then, to retrieve the information, simply reference curves. The z values depend on the order of the render queue (curves).
Of course, you probably could gather that data from the canvas, but I wouldn't recommend it.

How to (re)fill circle in canvas - requestAnimationFrame - HTML, JS

how can I fill the "new" canvas circle that appears next to the older one.
There is no problem with rectangle for example:
**
ctx.fillStyle = 'rgba('+quadratto.r+','+quadratto.g+','+quadratto.b+',1)';
quadratto.x += quadratto.speedX;
quadratto.y += quadratto.speedY;
quadratto.speedY += quadratto.speedY*(-0.15);
ctx.fillRect(quadratto.x-quadratto.h/4, quadratto.y-quadratto.h/2, 2, 2);**
What I want to do?
I'm creating animation in canvas where random-sized-color circle will appear and
it will move in a specified direction. The new canvas layaer will appear in the next frame (fps) with a new(old) circle.
var myCanvasPattern = document.createElement('canvas');
myCanvasPattern.width = window.innerWidth;
myCanvasPattern.height = window.innerHeight;
document.body.appendChild(myCanvasPattern);
var ryC = myCanvasPattern.getContext('2d');
function lottery(min, max) {
return Math.floor(Math.random()*(max-min+1))+min;
}
var allQuadro = [];
var fps = 50;
var lastTime = 0;
animationLoop();
function animationLoop(time){
requestAnimationFrame( animationLoop );
if(time-lastTime>=1000/fps){
lastTime = time;
for(var i=0;i<10;i++){
allQuadro.push({
r : lottery(0, 240),
g : lottery(0, 240),
b : lottery(0, 240),
circleR : lottery(10, 30),
x : myCanvasPattern.width/2,
y : myCanvasPattern.height/2,
speedX : lottery(-1000,1000)/100,
speedY : lottery(-1000,1000)/100
})
}
ryC.fillStyle = 'rgba(255,255,255,0.2)';
ryC.fill(0,0,myCanvasPattern.width, myCanvasPattern.height);
for(var i=0; i<allQuadro.length;i++){
var circle = allQuadro[i];
ryC.fillStyle = 'rgba('+circle.r+','+circle.g+','+circle.b+',1)';
circle.x += circle.speedX;
circle.y += circle.speedY;
//HERE's THE PROBLEM BELOW. HOW TO CREATE NEW ONE THAT APPEARS NEXT TO PREVIOUS ONE WITH NEW RANDOM COLOR
ryC.arc(circle.x-circle.circleR/2, circle.y-circle.circleR/2, circleR, 0, 2 * Math.PI);
//ryC.fill();
}
// ryC.fillStyle = 'rgba('+r+','+g+','+b+',1)';
//ryC.arc(x+speedX, y+speedY, circleR, 0, 2 * Math.PI);
//ryC.fill();
}
}
body {
margin: 0;
padding: 0;
overflow: hidden;
}
The fillRect() will fill directly to the canvas without going via a path (versus for example rect()).
The arc() on the other hand will add to a path which needs to be filled later. It also require the path to be cleared in-between the calls using beginPath().
A simple way to think about it is to wrap the necessary code into a function that acts like fillRect():
function fillArc() {
ctx.beginPath(); // clear current path
ctx.arc.apply(ctx, arguments); // apply arguments to arc() call
ctx.fill();
// notice that the arc still exist on the path after this call
// so to "truly" behave like fillRect() you could consider an
// additional beginPath() here.. it will depend on your code
}
In action:
var ctx = c.getContext("2d");
ctx.fillStyle = "#09f";
fillArc(70, 70, 70, 0, 6.28);
ctx.fillStyle = "#0a9";
fillArc(220, 70, 70, 0, 6.28);
function fillArc() {
ctx.beginPath();
ctx.arc.apply(ctx, arguments);
ctx.fill();
}
<canvas id=c></canvas>
If you are bold you can also add the method to the context itself before calling getContext():
CanvasRenderingContext2D.prototype.fillArc = function() {
this.beginPath();
this.arc.apply(this, arguments);
this.fill();
}
The use it like any other method:
ctx.fillArc( ... );
CanvasRenderingContext2D.prototype.fillArc = function() {
this.beginPath();
this.arc.apply(this, arguments);
this.fill();
}
var ctx = c.getContext("2d");
ctx.fillStyle = "#09f";
ctx.fillArc(70, 70, 70, 0, 6.28);
ctx.fillStyle = "#0a9";
ctx.fillArc(220, 70, 70, 0, 6.28);
<canvas id=c></canvas>

How to delete particular circle when user creates multiple circles by dragging mouse on screen using Javascript

I created circles using canvas but can't delete the same on double click.`
var canvas,
context, shapes,
dragging = false, draggingtoMove = false,
dragStartLocation,dragEndLocation,
snapshot;
var numShapes;
function initiate() {
numShapes = 100;
shapes = [];
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
canvas.addEventListener('mousedown', dragStart, false);
canvas.addEventListener('mousemove', drag, false);
canvas.addEventListener('mouseup', dragStop, false);
canvas.addEventListener('dblclick', dblclickerase);
}
function dblclickerase(evt)
{
var i;
//We are going to pay attention to the layering order of the objects so that if a mouse down occurs over more than object,
//only the topmost one will be dragged.
var highestIndex = -1;
//getting mouse position correctly, being mindful of resizing that may have occured in the browser:
var bRect = canvas.getBoundingClientRect();
mouseX = (evt.clientX - bRect.left)*(canvas.width/bRect.width);
mouseY = (evt.clientY - bRect.top)*(canvas.height/bRect.height);
//find which shape was clicked
for (i=0; i < shapes.length; i++) {
if (hitTest(shapes[i], mouseX, mouseY)) {
//draggingtoMove = true;
if (i > highestIndex) {
// here i want to delete the circle on double click but not getting logic, I can get mouse location but how to select the circle and delete it
}
}
}
}
function getCanvasCoordinates(event) {
var x = event.clientX - canvas.getBoundingClientRect().left,
y = event.clientY - canvas.getBoundingClientRect().top;
return {
x: x,
y: y
};
}
function takeSnapshot() {
snapshot = context.getImageData(0, 0, canvas.width, canvas.height);
}
function restoreSnapshot() {
context.putImageData(snapshot, 0, 0);
}
function draw(position) {
var radius = Math.sqrt(Math.pow((dragStartLocation.x - position.x), 2) + Math.pow((dragStartLocation.y - position.y), 2));
var i=0;
var tempX;
var tempY;
var tempRad;
var tempR;
var tempG;
var tempB;
var tempColor;
tempRad = radius;
tempX = dragStartLocation.x;
tempY = dragStartLocation.y;
tempColor = getRndColor();
tempShape = {x:tempX, y:tempY, rad:tempRad, color:tempColor};
shapes.push(tempShape);
context.beginPath();
context.arc(tempX, tempY, tempRad, 0, 2*Math.PI, false);
//context.closePath();
context.fillStyle = tempColor;
context.fill();
i++;
}
function dragStart(evt) {
dragging = true;
//if (dragging == true) {
dragStartLocation = getCanvasCoordinates(evt);
takeSnapshot();
//}
}
function hitTest(shape,mx,my) {
var dx;
var dy;
dx = mx - shape.x;
dy = my - shape.y;
//a "hit" will be registered if the distance away from the center is less than the radius of the circular object
return (dx*dx + dy*dy < shape.rad*shape.rad);
}
function drag(event) {
var position;
if (dragging === true) {
restoreSnapshot();
position = getCanvasCoordinates(event);
draw(position);
}
}
function dragStop(event) {
dragging = false;
restoreSnapshot();
var position = getCanvasCoordinates(event);
dragEndLocation = getCanvasCoordinates(event);
draw(position);
}
function getRndColor() {
var r = 255 * Math.random() | 0,
g = 255 * Math.random() | 0,
b = 255 * Math.random() | 0;
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
function eraseCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
addEventListener("load",initiate);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<canvas id="canvas" width="1020" height="640"></canvas>
<button onclick="eraseCanvas()" style="float: right;">Reset</button>
</body>
</html>
My question is how to delete the circle when double click on same, I
added 'dblClick' eventListener but still I am only able to perform the 'clearRect'
which will only clear the rectangle from start and end location which is little odd. Another thing I can't change the color to white which will not be valid.point as if my circle overlaps another will look odd.
You can't delete what you draw on the canvas like that. Once it's drawn on the canvas, it stays there and you have no way to access it except to read the pixel data - but that won't solve your problem because you can have overlapping circles of the same color.
To solve the issue, you must keep track of drawn circles, and redraw the full canvas every time it's needed (when adding a new circle, removing an old one, etc.). That way, when you want to delete a circle, you'd simply remove it from the list of circles (a simple array would work). But the important thing is that you need to clear and redraw the full canvas.
Summary: while having your canvas constantly redrawn (either on every tick or when a user interaction happens), your click'n'drag function should only be adding the circle to the circle list (specifying data like x, y, radius, color), while double-clicking a circle would look up that circle in the list, and remove it.

myContext.clearRect(0, 0, 500, 700); Not Clearing Canvas Correctly

I have an HTML5 Canvas that can be drawn on with the mouse. I would like to be able to clear the canvas so the user can make a new drawing. I do this with:
myContext.clearRect(0, 0, 500, 700);
The canvas appears clear but as soon as the user begins a new drawing the old drawing reappears. My JavaScript for the mouse drawing part is:
// Variables
var x1;
var y1;
var isPressed = false;
var myCanvas;
var myContext;
function startCanvas() {
// Canvas stuff
myCanvas = document.getElementById("can1");
myContext = myCanvas.getContext("2d");
// Specify a black background, and white lines that are 3 pixels thick.
myContext.fillStyle = '#000000';
myContext.strokeStyle = '#000000';
myContext.fillRect(0, 0, 500, 700);
myContext.lineWidth = 3;
myContext.fill();
}
function functionMouseDown(e) {
// Get coordinates
x1 = e.clientX - myCanvas.offsetLeft;
y1 = e.clientY - myCanvas.offsetTop;
isPressed = true;
}
function functionMouseMove(e) {
// If mouse is down and moved start drawing line
if (isPressed == true) {
drawLine(e);
}
}
function functionMouseUp() {
// Stop drawing line
isPressed = false;
}
function drawLine(e) {
// Draw line
var x = e.clientX - myCanvas.offsetLeft;
var y = e.clientY - myCanvas.offsetTop;
myContext.strokeStyle = '#ffffff';
myContext.lineWidth = 1;
myContext.moveTo(x1, y1);
myContext.lineTo(x, y);
myContext.stroke();
// Set start coordinates to current coordinates
x1 = x;
y1 = y;
}
startCanvas();
The HTML is:
<canvas id="can1" width="500" height="700"></canvas>
myContext.strokeStyle = '#000000';
myContext.beginPath();//<---- add this and read about this.
myContext.fillRect(0, 0, 500, 700);
myContext.lineWidth = 3; //why?
myContext.fill();

Categories