Canvas - Drawing second line to erase first one - javascript

So, I have one line that bounce from walls of my border while changing colours. But now I must create the second, white line, that will go like 3 or 4 seconds after first line erasing it. So it will be only 4 second lenght colour line bouncing from walls. And I have no idea how to do it. I already tried to use setTimeout, creating multiple functions etc.
var ctx=document.getElementById("canvas1").getContext("2d");
ctx.strokeStyle="red";
ctx.lineWidth=1;
var x=0
var y=0
var dx=1
var dy=1
function rysuj(){
ctx.strokeStyle="#"+((1<<24)*Math.random()|0).toString(16);
ctx.beginPath()
ctx.moveTo(x,y);
ctx.lineTo(x+dx,y+dy);
ctx.stroke();
if(x>200||x<0) dx=-dx;
if(y>150||y<0) dy=-dy;
x=x+dx;
y=y+dy;
}
setInterval ('rysuj()', 5);
<canvas id="canvas1" style="width:1000px; height:500px; border-style:solid;">
</canvas>

Looks like the key is to have two draw functions (note, this isn't ideal, but it works) and always have the "white" function explicitly set the strokeStyle. This is because the context for both draw functions is retrieved from the same canvas, and unless you explicitly set it on the "white" draw, it will be whatever it was from the previous "red" draw.
See below:
var getCanvasContext = function(strokeColor) {
var ctx = document.getElementById("canvas1").getContext("2d");
ctx.strokeStyle=strokeColor;
ctx.lineWidth=1;
return {ctx: ctx, x: 0, y:0, dx:1, dy:1, change: strokeColor !== "white"};
}
var drawRed = function(canvasContext) {
var ctx = canvasContext.ctx,
x = canvasContext.x,
y = canvasContext.y,
dx = canvasContext.dx,
dy = canvasContext.dy;
if (canvasContext.change) {
ctx.strokeStyle="#"+((1<<24)*Math.random()|0).toString(16);
}
ctx.beginPath();
ctx.moveTo(x,y);
ctx.lineTo(x+dx, y+dy);
ctx.stroke();
if(x>200||x<0) dx=-dx;
if(y>150||y<0) dy=-dy;
x=x+dx;
y=y+dy;
canvasContext.ctx = ctx;
canvasContext.x = x;
canvasContext.y = y;
canvasContext.dx = dx;
canvasContext.dy = dy;
};
var drawWhite = function(canvasContext) {
var ctx = canvasContext.ctx,
x = canvasContext.x,
y = canvasContext.y,
dx = canvasContext.dx,
dy = canvasContext.dy;
ctx.strokeStyle="#ffffff";
ctx.beginPath();
ctx.moveTo(x,y);
ctx.lineTo(x+dx, y+dy);
ctx.stroke();
if(x>200||x<0) dx=-dx;
if(y>150||y<0) dy=-dy;
x=x+dx;
y=y+dy;
canvasContext.ctx = ctx;
canvasContext.x = x;
canvasContext.y = y;
canvasContext.dx = dx;
canvasContext.dy = dy;
};
var redContext = getCanvasContext("red");
var whiteContext = getCanvasContext("white");
setInterval(function() {
drawRed(redContext);
}, 5);
setTimeout(function() {
setInterval(function() {
drawWhite(whiteContext)
}, 5);
}, 4000);
Hopefully this helps (see fiddle: JSFiddle Link)

Related

The canvas problem. ( ctx.clearRect does not working)

I want to create a break out game through javascript. I am wondering why the ctx.clearRect does not working. I want to put the rectangle in the y coordinate 430 to make it show at the bottom of the canvas. It moves when I have used the window.setInterval. But the rectangle move continuously.
Any help would be appreciated. Sorry for my poor English.
var canvas = document.getElementById("canvas");
const ctx = canvas.getContext('2d');
var position = 0;
var yposition = 430;
var length = 80;
var width = 20;
var xSpeed = length*1;
var ySpeed = 0;
function R(){
ctx.fillStyle = "green";
ctx.fillRect(position, yposition, length, width);
};
function C(){
position += xSpeed;
yposition += ySpeed;
};
window.setInterval(() => {
ctx.clearRect(0, 430, length, width);
R();
C();
},150);
ctx.beginPath();
ctx.arc(150, 50, 20, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = "blue";
ctx.fill();
The culprit are the parameters you're feeding into the clearRect function:
(0, 430, length, width)
Since length and width are hardcoded values of 80 and 20 respectively, the above means every time the intervals callback function gets fired it clears a rectangular area of 80 x 20 pixels at x = 0 and y = 430.
As your green paddle is moving you're actually clearing an area your paddle isn't located at anymore.
So you basically have two options:
Clear the whole canvas every frame
Clear the screen area your paddle has been before changing it's position
The second would look a little something like this:
var canvas = document.getElementById("canvas");
const ctx = canvas.getContext('2d');
var position = 0;
var yposition = 150;
var length = 80;
var width = 20;
var xSpeed = length * 1;
var ySpeed = 0;
function R() {
ctx.fillStyle = "green";
ctx.fillRect(position, yposition, length, width);
}
function C() {
position += xSpeed;
yposition += ySpeed;
}
window.setInterval(() => {
ctx.clearRect(position, yposition, length, width);
C();
R();
}, 500);
<canvas id="canvas" width="600" height="400"></canvas>
I'd definitely recommend clearing the whole canvas though since there will be other on-screen objects beside the paddle.

Remove point on double click on canvas

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>

Dynamically drawing in canvas

I'm trying to create a simple, purely JS program for drawing in a canvas. I have an acceptable solution right now, but if I draw very quickly, my drawing pen isn't continuous and I get scattered circles, probably because the computer can't keep up with the pace.
var draw = false;
function yesDraw() {
draw = true;
}
function mouseCoordinates(e) {
if (draw) {
var x = e.offsetX;
var y = e.offsetY;
drawing(x, y);
}
}
function noDraw() {
draw = false;
}
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
function drawing(x, y) {
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI);
ctx.fillStyle = "black";
ctx.fill();
}
<canvas id="myCanvas" height="400" ; width="1000" onmousedown="yesDraw()" onmousemove="mouseCoordinates(event)" onmouseup="noDraw()" onmouseout="noDraw()" style="border: solid 1px black;">Your browser does not support canvas.</canvas>
Is there any way to get a continuous flow of drawing and still keep it 100% JS?
Line segments from previous mouse position.
Rather than draw arcs draw line segments from the previous mouse position to the new one. As you don't want the line to be drawn from the end of the previous draw, you also need to indicate when a new line starts and set the previous mouse position to the current mouse.
To do this I added 3 variables lineStart, lastX,and lastY
lineStart is set to true when the mouse down event fires. In the draw function if line start is true then the lastX and lastY are set to the x,y and lineStart is set to false.
lastX, lastY hold the previous mouse position. They are set to x,y at the end of every draw call.
The line segments need to have the 2D context properties ctx.lineWidth set to the line width. To ensure the line is continuous the ctx.lineCap property is set to "round". This adds a semi circle to the start and end of the line.
Drawing past the edge
Having the pen turn off when the mouse moves out is annoying, you do this because if you don't you lose the mouse up events and the mouse keeps drawing with the mouse button up.
If you add the mouse to the document rather than the canvas, you don't have to worry about the mouse going outside the frame. You will still get the mouse up events even if the mouse is completely of the tab and browser.
Though you will have to use a slightly different way of getting the mouse coordinates, as you will want to still draw while off the canvas by at least half the draw line width. See code on how to get the mouse coordinates.
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
const r = 10; // draw radius
ctx.lineWidth = r * 2;
ctx.lineCap = "round";
ctx.fillStyle = "black";
var draw = false;
var lineStart = true;
var lastX, lastY;
function yesDraw() { draw = true; lineStart = true }
function mouseMove(e) {
const bounds = c.getBoundingClientRect();
const x = e.pageX - bounds.left - scrollX;
const y = e.pageY - bounds.top - scrollY;
if(draw && x > -r && x < c.width + r && y > -r && y < c.height + r){
drawing(x,y);
}
}
function noDraw() { draw = false }
document.addEventListener("mousemove",mouseMove);
document.addEventListener("mousedown",yesDraw);
document.addEventListener("mouseup",noDraw);
function drawing(x, y) {
if(lineStart){
lastX = x;
lastY = y;
lineStart = false;
}
ctx.beginPath();
ctx.lineTo(lastX, lastY);
ctx.lineTo(x, y);
ctx.stroke();
lastX = x;
lastY = y;
}
<canvas id="myCanvas" height="400" ; width="1000" style="border: solid 1px black;">Your browser does not support canvas.</canvas>
Use lineTo and end each line in an arc to make continuous smooth lines.
//Create Canvas
//(You can do this in HTML)
var c = document.body.appendChild(document.createElement("canvas"));
c.height = 400;
c.width = 1000;
var ctx = c.getContext("2d");
ctx.lineWidth = 20;
//Control drawing variable
var drawing = false;
c.onmousedown = c.onmouseout = c.onmouseup = function(evt) {
drawing = (evt.type === "mousedown");
};
//Keep track of last position
var oldX = null;
var oldY = null;
/**
* Draw the latest line
*
* #param {number} x
* #param {number} y
*/
function draw(x, y) {
if (drawing) {
if (oldX !== null) {
//If drawing, move to old coordinates for continual line
ctx.beginPath();
ctx.moveTo(oldX, oldY);
} else {
//Else simply move to current coordinates
ctx.moveTo(x, y);
ctx.beginPath();
}
//Draw a line
ctx.lineTo(x, y);
ctx.closePath();
ctx.stroke();
//Add an arc to the end of the line to make it smooth
ctx.beginPath();
ctx.arc(x, y, 0, 0, Math.PI * 2);
ctx.closePath();
ctx.stroke();
//Save new coordinates as old for next draw cycle
oldX = x;
oldY = y;
} else {
//If not drawing, cut line byt setting "old" to null
oldX = null;
oldY = null;
}
}
//Bind drawing
c.onmousemove = function(evt) {
draw(evt.offsetX, evt.offsetY);
};
canvas {
border: solid 1px black;
background-color: #eee;
}

flood fill code acting strangely

I wrote some code which will work like this:
grab the clicked point using variables x and y via e.layerX/y.
x value will be reduced by one and check if the canvas color is black or not.
if black it will stop otherwise continue.
The code I made should fill a single green line, but strangely it is filling the whole circle. It also covers up some of the black outline of my circle. Why is it so? How can I fix this?
Flood Fill Code:
canvas.addEventListener('click', fillit, false);
function fillit(e) {
var ctx = canvas.getContext('2d');
var x = e.layerX;
var y = e.layerY;
for (;;) {
ctx.fillStyle = "green";
ctx.fillRect(x, y, 1, 1);
ctx.fill();
var imageData = ctx.getImageData(x, y, 1, 1);
if (imageData.data[0] == 0 && imageData.data[1] == 0 && imageData.data[2] == 0) {
break;
}
x = x - 1;
}
}
Full Code:
css :
#mycircle{position:fixed;top:600px;left:0px;}
#vanishmycircle{position:fixed;top:600px;left:100px;}
html :
<form>
<input id="mycircle" type="button" value="make a circle"> <input id=
"vanishmycircle" type="button" value="delete a circle">
</form>
javascript :
function makeit() {
var canvas = document.createElement('canvas');
var atts = document.createAttribute('style');
canvas.height = 400;
canvas.width = 400;
atts.value = "border:1px solid black;";
canvas.setAttributeNode(atts);
document.body.appendChild(canvas);
document.getElementById('mycircle').addEventListener('click', acircle, false);
document.getElementById('vanishmycircle').addEventListener('click', notacircle, false);
function acircle(event) {
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.lineWidth = 10;
ctx.strokeStyle = "black";
ctx.arc(150, 150, 125, 0, 2 * Math.PI);
ctx.stroke();
}
function notacircle(event) {
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, 400, 400);
}
canvas.addEventListener('click', fillit, false);
function fillit(e) {
var ctx = canvas.getContext('2d');
var x = e.layerX;
var y = e.layerY;
for (;;) {
ctx.fillStyle = "green";
ctx.fillRect(x, y, 1, 1);
ctx.fill();
var imageData = ctx.getImageData(x, y, 1, 1);
if (imageData.data[0] == 0 && imageData.data[1] == 0 && imageData.data[2] == 0) {
break;
}
x = x - 1;
}
}
}
window.onload = makeit;
Call ctx.beginPath() before drawing the green pixel to reset the path. Without this call the path of the circle would also get filled with green.
When checking if the pixel is black you need to look at the next pixel, not the one that has just been painted. Use
var imageData=ctx.getImageData(x-1,y,1,1);
or move the decrement operation to before this call.
When testing the colour, you must also check that the alpha (imageData.data[3]) is 255.
I suggest you terminate the loop if x goes off the screen in case no black pixel is encountered.

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