Canvas 2d Only Displays Last Drawing in Series of Elements - javascript

I have a simple script that adds a new canvas to a div container for each clock that needs to be drawn based on the number of minutes given. The script will draw the canvas elements without fail and will draw the clock face in the last canvas element however it will not draw a clock face on any preceding canvas elements. Upon review of the console, the output is identical for the canvas elements both with and without the clock face. Not sure what is happening with this. Below is a JSFiddle with my code.
https://jsfiddle.net/1oxfgwkb/
The HTML
<html>
<body>
<div id="clockHolder"></div>
</body>
</html>
The JavaScript
var time = 115;
var holder = document.getElementById("clockHolder");
var clockNum = Math.ceil((time / 60));
for(var q = 1; q <= clockNum; q++) {
var clockFace = 'sample-'+q;
holder.innerHTML += '<canvas id="'+clockFace+'" width="35" height="35"></canvas>';
drawClock(clockFace);
}
function drawClock(clockFace) {
var c = document.getElementById(clockFace);
var ctx = c.getContext("2d");
var radius = (c.height / 2) * 0.9;
var midpoint = c.height / 2;
for(var i=1; i<=12; i++) {
var angle = (i*30) * Math.PI / 180;
ctx.save();
ctx.strokeStyle = 'rgba(100,100,100,0.75)';
ctx.beginPath();
ctx.translate(midpoint, midpoint);
ctx.rotate(angle);
ctx.translate(0, -radius*0.9);
ctx.moveTo(0,0);
ctx.lineTo(0,4);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
ctx.strokeStyle = '#555';
ctx.beginPath();
ctx.arc(midpoint, midpoint, radius*0.9, 0, 2 * Math.PI);
ctx.stroke();
}
Any help would be greatly appreciated.

It's drawing them both but setting the innerHTML of the parent is overwriting all but the last. Setting the innerHTML doesn't preserve existing elements, it makes new ones with identical attributes--but without whatever content you updated in the meantime.
Instead try:
for(var q = 1; q <= clockNum; q++) {
holder.innerHTML += '<canvas id="sample-'+q+'" width="35" height="35"></canvas>';
}
for(var q = 1; q <= clockNum; q++) {
drawClock('sample-'+q);
}
You could also use document.createElement and then holder.append.

Related

Javascript canvas behaving strangely in for loop

I'm trying to create a canvas graphic by rotating ellipses around a central point. When I do this however, the for loop creates a strange image. There are sporadic gaps in between ellipses, and the colors are strange. At first I thought I was calculating the angles wrong, but if that was the case, the gaps should not be sporadic as I am incrementing them in equal increments each iteration of the for loop. Then I thought maybe I was looping around too many times, but if that was the case, only the colors should be off and not the gaps. This makes me think it's an asynchronous problem where maybe the context didn't finish shifting before the next iteration started drawing the next circle, but that's just a guess. What could be wrong and how do I fix it?
Here's a jsfiddle, there's only 6 ellipses when there should be 10, what's happening?
https://jsfiddle.net/vj7csL3b/
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const ctx = canvas.getContext("2d");
let color = "#0000ff";
let numberOfEllipses = 10;
let lineThickness = 3;
let middleW = 150;
let middleL = 150;
let eWidth = 50;
let eHeight = 50;
let fill = "rgba(0,0,255,0.2)";
let shift = 50;
let rotate = ((Math.PI / 180) * 360) / 10;
//console.log(middleW, middleL, dfc);
for (let i = 1; i < 10 + 1; i++) {
//translate to point of rotation, rotate and translate back
ctx.translate(middleW, middleL);
ctx.rotate(rotate * i);
ctx.translate(-1 * middleW, -1 * middleL);
ctx.beginPath();
// draws ellipse shifted from the middle we are rotating around.
ctx.ellipse(
middleW + shift,
middleL + shift,
eWidth,
eHeight,
0,
0,
2 * Math.PI
);
ctx.strokeStyle = color;
ctx.lineWidth = lineThickness;
ctx.fillStyle = fill;
ctx.stroke();
ctx.fill();
}
</script>
</body>
</html>
The circles were actually overlapping over each other.Altering the rotation angle and shift will give the output that u expect.
const canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const ctx = canvas.getContext("2d");
let color = "#0000ff";
let numberOfEllipses = 10;
let lineThickness = 3;
let middleW = 150;
let middleL = 150;
let eWidth = 50;
let eHeight = 50;
let fill = "rgba(0,0,255,0.2)";
let shift = 60;
let rotate = ((Math.PI / 180) * 360) / 60;
//console.log(middleW, middleL, dfc);
for (let i = 1; i < 10 + 1; i++) {
//console.log("paiting circle" + i);
ctx.translate(middleW, middleL);
ctx.rotate(rotate * i);
ctx.translate(-1 * middleW, -1 * middleL);
ctx.beginPath();
ctx.ellipse(
middleW + shift,
middleL + shift,
eWidth,
eHeight,
0,
0,
2 * Math.PI
);
ctx.strokeStyle = color;
ctx.lineWidth = lineThickness;
ctx.fillStyle = fill;
ctx.stroke();
ctx.fill();
}

Draw dots on html5 canvas

I have a percentage that I want to represent as dots on a canvas. This is essentially a 10 x 10 matrix where I have a dot or image that represents one percent. so when it is 100% it will be green and when it is 10% only 10 of the dots will be green and the rest red.
Any idea what would be the best way to approach this problem?
something similar to this:
Except they should be circles/images instead of squares?
Here i created a simple example. Hope it'd be helpful to you.
var canvas = document.getElementById('mycanvas');
var context = canvas.getContext('2d');
var sizeX = canvas.width / 10;
var sizeY = canvas.height / 10;
var total = 15;
var count = 0;
for (var j = 0; j < 10; j++) {
for (var i = 0; i < 10; i++) {
context.beginPath();
context.arc(sizeX * (i+.5), sizeY * (j+.5), sizeX / Math.PI, 0, 2 * Math.PI, false);
context.fillStyle = total > count ? 'green' : 'red';
context.fill();
count++;
}
}
<canvas id="mycanvas" width="200" height="200" style="border:1px solid black;"></canvas>

Removing lines when drawing multiple arcs in a row in javascript

So I'm trying to draw 8 random circles (Or bubbles as I call them in this case) on the screen for a simple project I'm making. I want to make it so that I can show the bubbles, but there are lines connecting each bubble at its startAngle.
<!DOCTYPE html>
<html>
<head>
<title>Image</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-2.1.0.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var height = canvas.height;
var width = canvas.width;
ctx.strokeRect(0, 0, width, height);
var bubbles = [];
var Bubble = function () {
this.x = Math.floor(Math.random() * width);
this.y = Math.floor(Math.random() * height);
this.xspeed = 5;
this.yspeed = 5;
};
for (var i = 0; i < 8; i++) {
bubbles[i] = new Bubble();
};
Bubble.prototype.draw = function () {
for (var i = 0; i < 8; i++) {
ctx.fillStyle = "Black";
ctx.arc(bubbles[i].x, bubbles[i].y, 10, 0, Math.PI * 2, false);
}
};
setInterval(function () {
for (var i = 0; i < bubbles.length; i++) {
ctx.strokeStyle = "Black";
bubbles[i].draw();
ctx.stroke();
}
}, 30);
</script>
</body>
</html>
Basically, I've made 8 Bubble objects, then I've drawn an arc at their x and y (random) position, but it shows lines connecting them like so:
Output of code
When you run the code, it randomly generates 8 different locations and draws 8 arcs at their location, but it shows the lines that connect them. Is there a way to hide or get rid of these lines? I've tried to clear the canvas after each draw, but that hides the entire thing including the circle. I've been searching for most of the day and I couldn't find an answer to my specific problem. The answer is probably obvious, but I couldn't seem to find the solution due to my inexperience.
Call ctx.beginPath() before arc() to draw separate entities?
ctx.arc() creates an ctx.lineTo() between the last coordinates of the current path, and the first position of the arc.
So to avoid it, you can simply manually call moveTo() to lift the drawing pen to the first position of the arc.
This way, you can draw multiple arcs in the same Path and in the same stroke call (which becomes interesting when you draw a lot of arcs)
const ctx = c.getContext('2d'),
w = c.width,
h = c.height;
ctx.beginPath(); // call it once
for(let i = 0; i<100; i++){
let rad = Math.random()*20 + 5
let posX = rad + Math.random() * (w - rad *2);
let posY = rad + Math.random() * (h - rad *2);
// our arc will start at 3 o'clock
ctx.moveTo(posX + rad, posY); // so simply add 'rad' to the centerX
ctx.arc(posX, posY, rad, 0, Math.PI*2);
}
ctx.stroke(); // call it once
<canvas id="c" width="500"></canvas>

Trouble with javascript "for" loop

I have the following bit of code, which is supposed to write a series of triangles to a canvas element, but it appears to only go through the loop one time (only the first triangle is written to the canvas). I've looked at various similar questions, but am not having any success finding my problem, and was hoping a second pair of eyes might help.
I define array near the top of my script as follows:
var array = [0, 0.2, 0.4, 0.6, 0.8];
for (i = 0; i < array.length; i++)
{
ctx.beginPath();
ctx.moveTo(array[i],height);
ctx.lineTo(((width*0.075) + array[i]),0);
ctx.lineTo(((width*0.15 + array[i])),(height));
ctx.closePath();
ctx.fill();
}
Thank you all for the responses. It turns out that I misplaced my parenthesis. (width * (0.075 + array[i])) and so-on is what was intended. The following is intended behavior, on a canvas that is about 210 px * 30 px.
var array = [0, 0.2, 0.4, 0.6, 0.8];
for (i = 0; i < array.length; i++)
{
ctx.beginPath();
ctx.moveTo(width * array[i], height);
ctx.lineTo(width * (0.075 + array[i]),0);
ctx.lineTo(width * (0.15 + array[i]),(height));
ctx.closePath();
ctx.fill();
}
It actually does iterate through 5 times, but it just draws the same triangle over the previous ones, because the values in the array are so small. Try it with larger values:
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var array = [0,40,80,120,160];
var height = 150;
var width = 300;
for (i = 0; i < array.length; i++)
{
ctx.beginPath();
ctx.moveTo(array[i],height);
ctx.lineTo(((width*0.075) + array[i]),0);
ctx.lineTo(((width*0.15 + array[i])),(height));
ctx.closePath();
ctx.fill();
}
</script>
</body>
</html>
I took the basis for the code above from w3schools site because I've never used canvas before.
try to use closure
for (i = 0; i < array.length; i++)
{
(function(ind){
ctx.beginPath();
ctx.moveTo(array[ind],height);
ctx.lineTo(((width*0.075) + array[ind]),0);
ctx.lineTo(((width*0.15 + array[ind])),(height));
ctx.closePath();
ctx.fill();
})(i);
}
this should fix the problem.

How to create road lines using HTML5 canvas

I have created a road using canvas. There I wish to add lines in the middle. Also width, height and gap between lines must be increase accordingly.
<canvas id="myCanvas" width="578" height="500"></canvas>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var j = 0;
for(var i = canvas.height*.30; i< canvas.height; i=i+20){
context.beginPath();
context.rect(canvas.width*.50, i-j, 3+j, 10+(j*2));
context.fillStyle = '#000000';
context.fill();
j++;
}
But I couldn't make it by above code. Please help me to solve this.
jsfiddle
Updated : the following solution considers the skew in each line as well so it does not draw rectangles, instead polygons are used.
fiddle here : https://jsfiddle.net/tcdLf0xu/4/
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.clearRect(0,0,canvas.width,canvas.height);
var j = 0;
var y = 0;
for(var j = 1; y < canvas.height; j++){
context.beginPath();
spacing = 2.5
w = j+1;
h = 4*w;
x = canvas.width*.50 - w/2;
y = y + spacing*h;
context.rect(x,y ,w,h );
context.moveTo(x,y);
context.lineTo(x+w,y);
context.lineTo(x+w+1/spacing,y+h);
context.lineTo(x-1/spacing,y+h);
context.lineTo(x,y);
context.closePath();
context.fillStyle = '#000000';
context.fill();
}
Note that its important that you understand what each variable is doing in order to improve the design.
j is incremented linearly, is just a counter till your height is reached by y.
w, the width is increased linearly with each iteration of j.
h, the height is always 5 times the width (change that factor if you want)
x, the position is offset by half the width from the center.
y, would be a multiple of the height to accomodate white space efficiently, i took it as 2.5 but can adjust.

Categories

Resources