flood fill code acting strangely - javascript

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.

Related

Drawing Dynamic Gradients in HTML5 Canvas

I'm working on an art app and I want to be able to draw a gradient as a color. For example, if I keep drawing in a straight line or in circles, I want the gradient to repeat itself over and over. Right now the gradient is isolated to one side of the screen when I draw, but I would like to be able to draw with the gradient anywhere.
I have included the drawing function and color variable for reference.
HTML
<canvas id="canvas"></canvas>
JS
window.addEventListener('load', () => {
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
let painting = false;
var gradient = ctx.createLinearGradient(0, 0, 170, 0);
gradient.addColorStop("0", "magenta");
gradient.addColorStop("0.5", "blue");
gradient.addColorStop("1.0", "red");
function windowSize() {
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
}
function startPosition(e) {
painting = true;
draw(e);
}
function finishedPosition() {
painting = false;
ctx.beginPath();
}
function draw(e) {
if(!painting) return;
ctx.lineWidth = 10;
ctx.lineCap = "round";
ctx.strokeStyle = gradient;
ctx.lineTo(e.clientX, e.clientY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(e.clientX, e.clientY);
}
canvas.addEventListener('mousedown', startPosition);
canvas.addEventListener('mouseup', finishedPosition);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('touchstart', startPosition);
canvas.addEventListener('touchend', finishedPosition);
canvas.addEventListener('touchmove', draw);
window.addEventListener('resize', windowSize);
});
As I understand, you are trying to make a repeating-linear-gradient pattern.
It's supported in css, but not yet in the canvas gradient.
If all what you want to archive is a drawing like in your example, I will suggest you to do th following:
add a css gradient as a background and cover all unused space in white.
cover all the space in white
set the ctx.globalCompositeOperation = 'destination-out'; (so it will clean the drawing instead of draw)
Like this:
window.addEventListener('load', () => {
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'destination-out';
let painting = false;
function windowSize() {
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
}
function startPosition(e) {
painting = true;
draw(e);
}
function finishedPosition() {
painting = false;
ctx.beginPath();
}
function draw(e) {
if(!painting) return;
ctx.lineWidth = 10;
ctx.lineCap = "round";
ctx.lineTo(e.clientX, e.clientY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(e.clientX, e.clientY);
}
canvas.addEventListener('mousedown', startPosition);
canvas.addEventListener('mouseup', finishedPosition);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('touchstart', startPosition);
canvas.addEventListener('touchend', finishedPosition);
canvas.addEventListener('touchmove', draw);
window.addEventListener('resize', windowSize);
});
canvas {
background: repeating-linear-gradient(to right, magenta, blue, red, magenta) repeat-x;
background-size: 50px 100%;
}
<canvas id="canvas"></canvas>
--- EDIT ---
Also, you can use different (single) color at each event, instead of use gradient, and change the hue over time.
It will produce very different result. not sure what you like more..
Will be looking like that:
window.addEventListener('load', () => {
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
let colorIdx = 0;
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
let painting = false;
function getColor() {
colorIdx += 5;
return `hsl(${colorIdx}, 100%, 50%)`;
}
function windowSize() {
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
}
function startPosition(e) {
painting = true;
draw(e);
}
function finishedPosition() {
painting = false;
ctx.beginPath();
}
function draw(e) {
if(!painting) return;
ctx.lineWidth = 10;
ctx.lineCap = "round";
ctx.strokeStyle = getColor();
ctx.lineTo(e.clientX, e.clientY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(e.clientX, e.clientY);
}
canvas.addEventListener('mousedown', startPosition);
canvas.addEventListener('mouseup', finishedPosition);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('touchstart', startPosition);
canvas.addEventListener('touchend', finishedPosition);
canvas.addEventListener('touchmove', draw);
window.addEventListener('resize', windowSize);
});
<canvas id="canvas"></canvas>
Repeated gradient on 2D canvas
There are several ways to repeat a gradient. However they are all somewhat involved and will have some limitations and problems.
2 methods
The simplest is to manually repeat the color stops (see example) but suffers from floating point error
Use a pattern to repeat a gradient. Render a single repeat gradient to an offscreen canvas and use that canvas as a pattern, setting the pattern repeat appropriately. You will also have to align the gradient to the offscreen canvas axis and then set the pattern transform to match the orientation of the desired pattern.
Repeating color stops
The following function creates a gradient and adds a function to it that will create repeated color stops.
function RepeatingGradient(ctx, x1, y1, x2, y2, repeats) {
const dx = x2 - x1, dy = y2 - y1;
const gradient = ctx.createLinearGradient(x1, y1, x1 + dx * repeats, y1 + dy * repeats);
return Object.assign(gradient, {
addRepeatColorStop(pos, color) {
var i = 0;
const step = 1 / repeats;
const offset = pos / repeats;
while (i < repeats) {
const p = (i++) * step + offset;
// Rounding error may cause exception so check that p is not greater than 1
gradient.addColorStop(p > 1 ? 1 : p, color)
}
}
});
}
Usage
RepeatingLinearGradient(ctx, x1, y1, x2, y2, repeats) It needs a 2D context ctx, then the standard linear gradient arguments, x1, y1, x2, y2 and then the number of repeats repeats.
The repeats extend the area of the gradient. Thus if the positioning arguments are 0,0,0,10 and the repeat is 10 then the gradient will cover the area 0,0,0,100
You add repeating color stops using the new function gradient.addRepeatColorStop(pos, color)
You use the resulting gradient as normal
const grad = RepeatingGradient(ctx, 0,0,0,10, 10);
grad.addRepeatColorStop(0, "#000");
grad.addRepeatColorStop(1, "#FFF");
ctx.strokeStyle = grad;
Example use
Use mouse to draw using repeated gradient.
function RepeatingGradient(ctx, x1, y1, x2, y2, repeats) {
const dx = x2 - x1;
const dy = y2 - y1;
const gradient = ctx.createLinearGradient(x1, y1, x1 + dx * repeats, y1 + dy * repeats);
return Object.assign(gradient, {
addRepeatColorStop(pos, color) {
var i = 0;
const step = 1 / repeats, offset = pos / repeats;
while (i < repeats) {
const p = (i++) * step + offset;
gradient.addColorStop(p > 1 ? 1 : p, color);
}
}
});
}
const lineWidth = 20;
const ctx = canvas.getContext('2d');
canvas.height = innerHeight;
canvas.width = innerWidth;
ctx.lineWidth = lineWidth;
ctx.lineCap = "round";
const gradient = RepeatingGradient(ctx, 0, 0, 80, 80, 20);
gradient.addRepeatColorStop(0, "#F00");
gradient.addRepeatColorStop(0.25, "#FF0");
gradient.addRepeatColorStop(0.5, "#0F0");
gradient.addRepeatColorStop(0.75, "#FF0");
gradient.addRepeatColorStop(1, "#F00");
ctx.strokeStyle = gradient;
const mouse = {x : 0, y : 0, ox: 0, oy: 0, button : false, updateFunc: undefined}
mouse.updateFunc = function draw() {
if (mouse.button) {
ctx.beginPath();
ctx.lineTo(mouse.ox, mouse.oy);
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
}
}
function mouseEvents(e){
const bounds = canvas.getBoundingClientRect();
mouse.ox = mouse.x;
mouse.oy = mouse.y;
mouse.x = e.pageX - bounds.left - scrollX;
mouse.y = e.pageY - bounds.top - scrollY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
mouse.updateFunc && mouse.updateFunc();
}
["down","up","move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>
Click drag mouse to draw.
Notes
The repeat is not infinite. You need to ensure that you cover all of the area you want painted.
Repeats will reduce performance by a small amount depending on the number of repeats.
Gradients use FLOATS (32 bit floating point) not DOUBLES (64 bit floating point). Adding too many repeats and you will start to get some artifacts (not all repeats are identical, some stops may be out of order). Try to keep the number of repeats to the minimum needed to fit the render area. (FLOAT is the upper size, low end devices may only support lower than 32bit precision floating point)
If rounding errors start to effect the quality, try adding repeat stop ends slightly off 0 and 1
eg
gradient.addRepeatColorStop(0.01, "#F00"); // slightly above
gradient.addRepeatColorStop(0.99, "#F0F"); // slightly below
For the best results try to have the first and last position in the repeat match each other.
eg
gradient.addRepeatColorStop(0, "#F00"); // start
gradient.addRepeatColorStop(0.5, "#0F0"); // mid
gradient.addRepeatColorStop(1, "#F00"); // Match start
Using pattern to repeat gradients
The next method uses a pattern. Warning calling this function too often can result in a out of memory exception (gecko)
The function
function RepeatingLinearGradient(x1, y1, x2, y2) {
const dx = x2 - x1;
const dy = y2 - y1;
const length = (dx * dx + dy * dy) ** 0.5 | 0;
const can = document.createElement("canvas"); // support for OffscreenCanvas() limited
can.width = length;
can.height = 1;
const ctxP = can.getContext("2d");
const nx = dx / length;
const ny = dy / length;
const matrix = new DOMMatrixReadOnly([nx, ny, -ny, nx ,x1, y1]);
const gradient = ctxP.createLinearGradient(0, 0, length, 0);
var dirty = true;
function update() {
ctxP.fillStyle = gradient;
ctxP.fillRect(0,0,length, 1);
var pattern;
Object.assign(pattern = ctxP.createPattern(can, "repeat"), {
addColorStop(pos, color) {
gradient.addColorStop(pos, color);
return update();
}
});
pattern.setTransform(matrix);
return pattern;
}
return update();
}
The function creates an off screen canvas and renders an axis align gradient to it. Each time you add a color stop a new pattern is created and returned.
To align the pattern with the desired gradient the patterns transform is set to match the gradients orientation.
Usage
Similar to a normal gradient but returns a pattern. Arguments are two coordinate pairs x1, y1, x2, y2
IMPORTANT As patterns are not live you must reassign the gradient every time you make a change (eg add color stop)
var gradient = RepeatingLinearGradient(0, 0, 80, 80);
/* MUST!!! reassign */
gradient = gradient.addColorStop(0, "#000"); // MUST!!! reassign
gradient = gradient.addColorStop(1, "#FFF"); // MUST!!! reassign
ctx.strokeStyle = gradient;
Example
Use mouse to draw
function RepeatingGradient(x1, y1, x2, y2) {
const dx = x2 - x1;
const dy = y2 - y1;
const length = (dx * dx + dy * dy) ** 0.5 | 0;
const can = document.createElement("canvas");
can.width = length;
can.height = 1;
const ctxP = can.getContext("2d");
const nx = dx / length;
const ny = dy / length;
const matrix = new DOMMatrixReadOnly([nx, ny, -ny, nx ,x1, y1]);
const gradient = ctxP.createLinearGradient(0, 0, length, 0);
var dirty = true;
function update() {
ctxP.fillStyle = gradient;
ctxP.fillRect(0,0,length, 1);
var pattern;
Object.assign(pattern = ctxP.createPattern(can, "repeat"), {
addColorStop(pos, color) {
gradient.addColorStop(pos, color);
return update();
}
});
pattern.setTransform(matrix);
return pattern;
}
return update();
}
const lineWidth = 20;
const ctx = canvas.getContext('2d');
canvas.height = innerHeight;
canvas.width = innerWidth;
ctx.lineWidth = lineWidth;
ctx.lineCap = "round";
var gradient = RepeatingGradient(0, 0, 40, 20);
gradient = gradient.addColorStop(0, "#F00");
gradient = gradient.addColorStop(0.25, "#FF0");
gradient = gradient.addColorStop(0.5, "#0F0");
gradient = gradient.addColorStop(0.75, "#FF0");
gradient = gradient.addColorStop(1, "#F00");
ctx.strokeStyle = gradient;
const mouse = {x : 0, y : 0, ox: 0, oy: 0, button : false, updateFunc: undefined}
mouse.updateFunc = function draw() {
if (mouse.button) {
ctx.beginPath();
ctx.lineTo(mouse.ox, mouse.oy);
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
}
}
function mouseEvents(e){
const bounds = canvas.getBoundingClientRect();
mouse.ox = mouse.x;
mouse.oy = mouse.y;
mouse.x = e.pageX - bounds.left - scrollX;
mouse.y = e.pageY - bounds.top - scrollY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
mouse.updateFunc && mouse.updateFunc();
}
["down","up","move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>
Click drag mouse to draw.
Notes
Does not suffer the same floating point issues as the first method.
Is slower when creating patterns and uses more memory.
Can throw memory error if creating too often or adding stops too often.
Repeats are infinite, You can not limit the number of repeats.
Uses DOMMatrixReadOnly this may not be supported by all browsers.

Is it possible to make the text disappears from left to right in a canvas?

I would like to make the text in a canvas to appears and disappers from left to right. I don't know if it's possible. I tried to hide the text with the clip() function, but I can't transform the clip to reduce the width and make the text appears.
ctx.fillStyle = "#1f2f90";
ctx.fillText('This is a text!', 150, 100);
ctx.rect(50, 20, 200, 120);
ctx.stroke();
ctx.clip();
var i = 200;
setInterval(function(){
ctx.clearRect(0,0,300,150);
ctx.rect(50,20,i,120);
ctx.stroke();
ctx.clip();
i++;
},20);
Here is an example using fillRect to cover the text.
let ctx = document.getElementById('canvas').getContext('2d');
ctx.fillStyle = "#1f2f90";
ctx.fillText('This is a text!', 150, 100);
var i = 0;
setInterval(function(){
ctx.fillStyle = "white";
ctx.fillRect(50,20,i,120);
ctx.strokeRect(50, 20, 200, 120);
i++;
},20);
<canvas id="canvas"></canvas>
My solution is different. Every letter is an object with a position and a transparency alpha. During the animation the transparency of of the letters is decreasing, one letter at a time.
You may restart the animation on click.
Please read the comments in the code.
const canvas = document.getElementById("canvas");
const _canvas = document.getElementById("_canvas");
const ctx = canvas.getContext("2d");
const _ctx = _canvas.getContext("2d");
let cw = canvas.width = _canvas.width = 400,
cx = cw / 2;
let ch = canvas.height = _canvas.height = 150,
cy = ch / 2;
let rid = null;// request animation id
let theText = 'This is a text!';
let letters = [];// the letters array
let k = 20;// controls the speed of the animation
ctx.font = _ctx.font = "2em Verdana";
// every letter is an object
class Letter{
constructor(char,x){
this.char = char;// the letter
// measure the letter
_ctx.fillText(this.char, 0, cy);
this.w = _ctx.measureText(this.char).width;
//the position of the text
this.pos = {}
this.pos.y = cy;
this.pos.x = x;
// the transparency of the letter
this.alpha = 1;
}
show(){// draw the letter
ctx.fillStyle = `rgba(0,0,0,${this.alpha})`;
ctx.fillText(this.char, this.pos.x, this.pos.y);
}
update(){
//change the transparency of the text
if(this.alpha > 0){this.alpha -= 1/k;}
if(this.alpha < 0){this.alpha = 0; index++}
}
}
let x = 0;
for(l=0; l<theText.length; l++){
// a new letter object
letters.push(new Letter(theText[l],x))
//calculate the x position of the next letter
x = letters.reduce( function(a, b){return a + b.w}, 0);
}
// draw all the letters
for(l=0; l<letters.length; l++){letters[l].show()}
// the actual letter.
let index = 0;
function Draw() {
rid = window.requestAnimationFrame(Draw);
ctx.clearRect(0,0,cw,ch);
letters[index].update();//change the transparency of the actual letter
// draw all the letters
for(l=0; l<letters.length; l++){letters[l].show()}
// if the last letter is fully transparent stop the animation
if(letters[letters.length - 1].alpha <= 0){
window.cancelAnimationFrame(rid);rid = null;
}
}
Draw();
//resume animation on click
canvas.addEventListener("click",()=>{
if(rid){window.cancelAnimationFrame(rid);rid = null;}
index = 0;
for(l=0; l<letters.length; l++){letters[l].alpha = 1;letters[l].show()}
Draw();
})
#_canvas{display:none;}
canvas {
border:1px solid;
}
<canvas id="canvas"></canvas>
<canvas id="_canvas"></canvas>

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>

Canvas - Drawing second line to erase first one

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)

How to rotate an image on HTML5 canvas when the right and left arrow keys are pressed

I need help trying to rotate the rectangle that I have drawn on the canvas. I would like the top of the rectangle to pivot either to the right or left once I press on the arrow keys on my keyboard. This is my code so far:
HTML:
<body >
<div id="canvas-container">
<canvas id="canvas" width="500" height="400"></canvas>
</div>
</body>
CSS:
canvas {
display: inline;
}
Javascript:
document.addEventListener("DOMContentLoaded", function() {
drawBorder();
});
var canvas;
var context;
var size;
drawRectangle();
drawHalfCircle();
function drawBorder() {
canvas = document.getElementById("canvas");
context = canvas.getContext('2d');
size = {
x: canvas.width,
y: canvas.height
};
//have to set colors etc befor it is drawn
context.strokeStyle = 'black';
//takes 4 parameters
context.strokeRect(0, 0, size.x, size.y);
}
function drawRectangle() {
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
ctx.rect(246, 290, 8, 80);
ctx.stroke();
}
function drawHalfCircle(){
var c= document.getElementById("canvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.arc(250,579,308,1.2*Math.PI, 1.8*Math.PI);
ctx.stroke();
}
I have mocked something up is this along the correct lines of what you are wanting.
document.addEventListener("DOMContentLoaded", function() {
drawBorder();
});
var canvas = document.getElementById("canvas");
var context = canvas.getContext('2d');
var size;
var angle = 0;
setInterval(function () {
context.save();
context.clearRect(0, 0, canvas.width, canvas.height);
drawBorder();
drawHalfCircle();
drawRectangle();
context.restore();
}, 100);
function drawBorder() {
size = {
x: canvas.width,
y: canvas.height
};
//have to set colors etc befor it is drawn
context.strokeStyle = 'black';
//takes 4 parameters
context.strokeRect(0, 0, size.x, size.y);
}
function drawRectangle() {
context.rotate(Math.PI / 180 * (angle));
context.rect(246, 290, 8, 80);
context.stroke();
}
function drawHalfCircle(){
context.beginPath();
context.arc(250,579,308,1.2*Math.PI, 1.8*Math.PI);
context.stroke();
}
document.onkeydown = function(e) {
var event = window.event ? window.event : e;
if (e.keyCode == '37') {
angle += 5;
}
else if (e.keyCode == '39') {
angle -= 5;
}
}
Basically set an interval and redraw (ie frames like in a movie) and rotate via a variable.
See a demo here
https://jsbin.com/qititacazu/edit?js,output
If you want to translate it so it will rotate around a different point do something like this.
context.translate(246, 290);
context.rotate(Math.PI / 180 * (angle));
context.rect(-4, 0, 4, 80);

Categories