How to make multiple circles with Canvas JS - javascript

I have a function that makes a circle when I click on a button but if I keep clicking the button, it just copies the circle in the same place. I'm trying to figure out a way to make multiple circles with different xy positions. I have the code below
function nerdsAssemble(){
var canvas = document.getElementById("boop");
if (canvas.getContext)
{
var con=canvas.getContext("2d");
var W=canvas.width / 2;
var H=canvas.height / 2;
var rad=45;
con.beginPath();
con.arc(W, H, rad, 0, 2 * Math.PI, false);
con.stroke();
}
}
}

At the moment you're putting the circle at a pre-determined location:
var W=canvas.width / 2;
var H=canvas.height / 2;
This is the center of the canvas element. As a side note - the variable names W and H are a bit misleading because the corresponding parameters of the arc() function are actually the x and y position. Anyway, If you want to have circles at random positions, you can utilize the Math.random() function. It returns a random number between 0 and 1. If you multiply that by the width / height of the canvas you'll have circles all around the canvas.
function nerdsAssemble() {
var canvas = document.getElementById("boop");
if (canvas.getContext) {
var con = canvas.getContext("2d");
var W = Math.random() * canvas.width;
var H = Math.random() * canvas.height;
var rad = 45;
con.beginPath();
con.arc(W, H, rad, 0, 2 * Math.PI, false);
con.stroke();
}
}
document.getElementById("myButton").addEventListener("click", nerdsAssemble);
<canvas id="boop" width="400" height="300"></canvas>
<button id="myButton">
Circle
</button>
Followup
If you want new circles appear next to each other from left to the right we need to change some things.
First we need some variables that hold the position of a circle. Let's call those xPosition and yPosition. The initial value of xPosition should be 45. Why 45? 45 is the radius of the circle as determined by the equally named variable radius and a horizontal position of 45 means we're touching the left side of the canvas.
Now if we increment the xPosition by radius * 2 (90, is the diameter of the circle) after drawing a circle, the next circle will be next to the last one.
Here's some code:
var canvas = document.getElementById("boop");
var radius = 45;
var xPosition = radius;
var yPosition = canvas.height / 2;
var con = canvas.getContext("2d");
function nerdsAssemble() {
con.beginPath();
con.arc(xPosition, yPosition, radius, 0, 2 * Math.PI, false);
con.stroke();
xPosition += radius * 2;
}
document.getElementById("myButton").addEventListener("click", nerdsAssemble);
<canvas id="boop" width="400" height="300"></canvas>
<button id="myButton">
Circle
</button>

Related

Using trigonometry to animate HTML5 canvas, but how to position this square?

Sometimes you wish you could go back in time to tell your younger self that maths are indeed important! But I doubt I would've listened back then. I've been playing with trigonometry lately for animation purposes with the HTML5 canvas in this particular example.
It's a super simple animation: It positions an arc in a circular manner around the center of the canvas. The X and Y positions are calculated based upon the basic trigonometry functions sinus and cosinus. "SohCahToa". I think I'm starting to get it. But somehow I cannot figure out how to draw a square in the middle of one of the triangular sides.
let radius = 200;
let angle = 0;
x = centerX + Math.cos(angle) * radius;
ctx.beginPath();
ctx.fillRect(x/2, centerY, 20, 20);
https://codepen.io/melvinidema/pen/wvKPepa?editors=1010
So the arc is drawn by adding up the center of the canvas with the
rework formulas: (co)sinus - for X = Cos, for Y = Sin) of the angle times the radius of the circle.
If we only take the X position (red line) and want to draw the square half of the position of the arc. ( So in the middle of the red line ) we should just be able to divide the freshly calculated X position by two right? But if I do that, the square is magically drawn completely outside the circle.
What's happening? Why does it behave like this? And what calculation should I use instead for the square to position in the middle of the red line throughout the animation?
Thanks in advance!
Your x defined as:
x = centerX + Math.cos(angle) * radius;
but when you want to divide by 2, you just need to divide the Math.cos(angle) * radius, while the centerX is the zero point, and its stand as it is.
So the rect should be placed at:
centerX + Math.cos(angle)/2
Also, I think will be better if you reduce half of the rect width, and get:
centerX + Math.cos(angle)/2 - 10
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
let radius = 200;
function frame(angle) {
const cx = canvas.width / 2,
cy = canvas.height / 2,
x = Math.cos(angle) * radius,
y = Math.sin(angle) * radius;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(cx+x, cy+y);
ctx.lineTo(cx+x, cy);
ctx.lineTo(cx, cy);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.arc(cx+x, cy+y, 10, 0, Math.PI*2);
ctx.fill();
ctx.fillRect(cx + x/2 - 10, cy - 10, 20, 20);
ctx.closePath();
requestAnimationFrame(()=>frame(angle+.03));
}
frame(0)
canvas {
display: block;
max-height: 100vh;
margin: auto;
}
<canvas width="500" height="500"></canvas>

html5 canvas draw lines in a circle

I am having some trouble drawing lines in circle with html5 canvas.
I am trying to make the bars look something like this
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
var bars = 50;
var radius = 100;
for(var i = 0; i < bars; i++){
var x = radius*Math.cos(i);
var y = radius*Math.sin(i);
draw_rectangle(x+200,y+200,1,13,i, ctx );
}
function draw_rectangle(x,y,w,h,deg, ctx){
ctx.save();
ctx.translate(x, y);
ctx.rotate(degrees_to_radians(deg));
ctx.fillStyle = "yellow";
ctx.fillRect(-1*(w/2), -1*(h/2), w, h);
ctx.restore();
}
function degrees_to_radians(degrees){
return degrees * Math.PI / 180;
}
function radians_to_degrees(radians){
return radians * 180 / Math.PI;
};
for some reason my lines are all crooked and unaligned. I really need help on this one. https://codepen.io/anon/pen/PRBdYV
The easiest way to deal with such a visualization is to play with the transformation matrix of your context.
You need to understand it as if you were holding a sheet of paper in your hands.
Instead of trying to draw the lines at the correct angle, rotate the sheet of paper, and always draw your lines in the same direction.
This way all you need in your drawing method is the angle, and the height of each bar.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
// the position of the whole thing
var circleX = canvas.width / 2;
var circleY = canvas.height / 2;
//
var bars = 50;
var barWidth = 5;
// inner radius
var radius = 50;
ctx.fillStyle = "yellow";
// no need to use degrees, a full circle is just 2π
for(var i = 0; i < Math.PI*2; i+= (Math.PI*2 / bars)){
draw_rectangle(i, (Math.random()*30) + 10);
}
function draw_rectangle(rad, barHeight){
// reset and move to the center of our circle
ctx.setTransform(1,0,0,1, circleX, circleY);
// rotate the context so we face the correct angle
ctx.rotate(rad);
// move along y axis to reach the inner radius
ctx.translate(0, radius);
// draw the bar
ctx.fillRect(
-barWidth/2, // centered on x
0, // from the inner radius
barWidth,
barHeight // until its own height
);
}
canvas#canvas{
background:black;
}
<html>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
</body>
</html>
https://codepen.io/anon/pen/YajONR
Problems fixed: Math.cos wants radians, not degrees
We need to go from 0 to 360, so I adjusted the number of bars to make that a bit easier, and multiplied i by 6 (so the max value is 60*6==360)
If we don't add +90 when drawing the bars, we just get a circle
Check your codepen and figured out the problem lies in the degrees_to_radians
Here is the update link of you code.Link
PS I only looked at the shape of the circle not alignments of the bar :D

Drawing a angle / arc, filled with a radiant in canvas?

Problem: Im drawing a spaceship on the canvas. Upon hovering over it's x/y, im drawing an arc on the canvas, indicating the starships weapons angle and range (considering the starships current Baring/facing). Currently the determined angle is being drawn in green and extends as far as the weapons range value allows.
However, i would like to use a gradiant to fill the determined arc to indicate a drop-off in accuracy (i.e. gradiant begins at green, moves to orange, turns red the further away from the starships Position the angle is).
However, i dont know how i could replace my stock ctx.fill() on the drawn arc with a gradiant.
var ship {
loc: {x, y}, // lets say 100, 100
facing: facing // lets say facing 0, i.e. straight right
weapons: objects (range, startArc, endArc) // lets say 50, 300, 60 -> 120 degree angle, so -60 and +60 from facing (0/360)
}
for (var i = 0; i < weapon.arc.length; i++){
var p1 = getPointInDirection(weapon.range, weapon.arc[i][0] + angle, pos.x, pos.y);
var p2 = getPointInDirection(weapon.range, weapon.arc[i][1] + angle, pos.x, pos.y)
var dist = getDistance( {x: pos.x, y: pos.y}, p1);
var rad1 = degreeToRadian(weapon.arc[i][0] + angle);
var rad2 = degreeToRadian(weapon.arc[i][1] + angle);
fxCtx.beginPath();
fxCtx.moveTo(pos.x, pos.y);
fxCtx.lineTo(p1.x, p1.y);
fxCtx.arc(pos.x, pos.y, dist, rad1, rad2, false);
fxCtx.closePath();
fxCtx.globalAlpha = 0.3;
fxCtx.fillStyle = "green";
fxCtx.fill();
fxCtx.globalAlpha = 1;
}
is it possible to replace the arc/globalalpha/fill so use a gradiant flow instead of it being colored fixed and if so, how ?
thanks
To fill an arc with a gradient, animated just for the fun.
Uses a radial gradient and set colour stops as a fraction of distance.
The function createRadialGradient takes 6 numbers the position x,y and start radius and the position x,y and end radius of the gradient.
Colour stops are added via the gradient object addColorStop function that takes a value 0 inner to 1 outer part of the gradient and the colour as a CSS color string. "#F00" or "rgba(200,0,0,0.5)" or "RED"
Then just use the gradient as the fill style.
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");
function update(time) {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// position of zones in fractions
var posRed = 0.8 + Math.sin(time / 100) * 0.091;
var posOrange = 0.5 + Math.sin(time / 200) * 0.2;
var posGreen = 0.1 + Math.sin(time / 300) * 0.1;
var pos = {
x: canvas.width / 2,
y: canvas.height / 2
};
var dist = 100;
var ang1 = 2 + Math.sin(time / 1000) * 0.5;
var ang2 = 4 + Math.sin(time / 1300) * 0.5;
var grad = ctx.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, dist);
grad.addColorStop(0, "#0A0");
grad.addColorStop(posGreen, "#0A0");
grad.addColorStop(posOrange, "#F80");
grad.addColorStop(posRed, "#F00");
grad.addColorStop(1, "#000");
ctx.fillStyle = grad;
ctx.beginPath();
ctx.moveTo(pos.x, pos.y);
ctx.arc(pos.x, pos.y, dist, ang1, ang2);
ctx.fill();
requestAnimationFrame(update);
}
requestAnimationFrame(update);

Drawing many circles to form a larger shape

I'm making a project using canvas and svg. I've drawn a pattern using canvas with 4 Circles and in each 4 circles there's one inner circle. The problem is, I now need to make those 4 circles and inner circles smaller in order to insert more of them, up to 30, on my screen. Here's my code.
function telaCirculos(x,y,r,angIn,angFim,corFundo,corLinha){
pintor.fillStyle=corFundo;
pintor.strokeStyle=corLinha;
pintor.beginPath();
pintor.arc(x,y,r,angIn,angFim);
pintor.closePath();
pintor.stroke(); pintor.fill();
}
then I just call my function in the script like so:
telaCirculos(250,500,250,Math.PI,-2*Math.PI,"#449779","#449779");
telaCirculos(250,500,200,Math.PI,-2*Math.PI,"#013D55","#013D55");
telaCirculos(500,250,250,Math.PI/2,3*Math.PI/2,"#E6B569","#E6B569");
telaCirculos(500,250,200,Math.PI/2,3*Math.PI/2,"#AA8D49","#AA8D49");
telaCirculos(0,250,250,Math.PI/2,-3*Math.PI/2,"#E6B569","#E6B569");
telaCirculos(0,250,200,Math.PI/2,-3*Math.PI/2,"#AA8D49","#AA8D49");
telaCirculos(250,0,250,0,-Math.PI,"#449779","#449779");
telaCirculos(250,0,200,0,-Math.PI,"#013D55","#013D55");
This draws the circles with my desired coordinates. Now I need to fill my screen with more of these. I'll post some screenshots.
What I have done:
What I need to do:
An alternative to GameAlchemist's solution, is to think of the pattern as rows and columns of circles. You can use nested loops to draw the rows and columns of circles. Each row partially overlaps the previous row by half a circle. Every other row offset is horizonally offset by half a circle. Since rows overlap, each row covers a vertical distance of radius. To compute number of rows, you basically divide height by radius. Since the columns do not overlap, each column covers a horizonal distance of 2 * radius. To compute number of columns, you basically divide width by radius. Since the first circle can be half outside the area being painting, you actually have to add radius to height and width before dividing by 2 * radius and radius, respectively. You can use arrays to hold the colors and offsets. Then the function to fill a rectangular area with circles could look like...
function drawCircles(x, y, width, height, outerRadius, innerRadius) {
var outerColors = ["#449779", "#E6B569"];
var innerColors = ["#013d55", "#AA8D49"];
var offsets = [0, outerRadius];
var startAngle = 0;
var endAngle = 2 * Math.PI;
var iMax = (width + outerRadius) / (outerRadius);
for (i = 0; i < iMax; i++) {
var outerColor = outerColors[i % outerColors.length];
var innerColor = innerColors[i % innerColors.length];;
var offset = offsets[i % offsets.length];
var jMax = (height + outerRadius - offset) / (2 * outerRadius);
for (j = 0; j < jMax; j++) {
var cx = x + j * 2 * outerRadius + offset;
var cy = y + height - i * outerRadius;
telaCirculos(cx, cy, outerRadius, startAngle, endAngle, outerColor, outerColor)
telaCirculos(cx, cy, innerRadius, startAngle, endAngle, innerColor, innerColor);
}
}
}
You would then call the function with the dimensions of the canvas and the desired size of circles...
var outerRadius = canvasWidth / numberOfCircles;
var innerRadius = 0.8 * outerRadius;
drawCircles(0, 0, canvasWidth, canvasHeight, outerRadius, innerRadius);
Your best take here is to create a pattern out of your circle, then to fill your main canvas with that pattern.
To do that, create a temporary canvas on which you draw your pattern then create a pattern out of it.
document.body.style.margin = 0;
var cv = document.getElementById('cv');
var mainContext = cv.getContext('2d');
var canvasWidth;
var canvasHeight;
function update() {
canvasWidth = cv.width = window.innerWidth;
canvasHeight = cv.height = window.innerHeight;
drawCircles();
}
update();
window.onresize = update;
function drawCircles() {
mainContext.save();
// full screen rect
mainContext.rect(0, 0,
mainContext.canvas.width, mainContext.canvas.height);
// scale to put more circles inside ( :-) )
mainContext.scale(1 / 10, 1 / 10);
mainContext.fillStyle = buildPattern();
mainContext.fill();
mainContext.restore();
}
function buildPattern() {
var tempCv = document.createElement('canvas');
tempCv.width = 500;
tempCv.height = 500;
var pintor = tempCv.getContext('2d');
telaCirculos(250, 500, 250, Math.PI, -2 * Math.PI, "#449779", "#449779");
telaCirculos(250, 500, 200, Math.PI, -2 * Math.PI, "#013D55", "#013D55");
telaCirculos(500, 250, 250, Math.PI / 2, 3 * Math.PI / 2, "#E6B569", "#E6B569");
telaCirculos(500, 250, 200, Math.PI / 2, 3 * Math.PI / 2, "#AA8D49", "#AA8D49");
telaCirculos(0, 250, 250, Math.PI / 2, -3 * Math.PI / 2, "#E6B569", "#E6B569");
telaCirculos(0, 250, 200, Math.PI / 2, -3 * Math.PI / 2, "#AA8D49", "#AA8D49");
telaCirculos(250, 0, 250, 0, -Math.PI, "#449779", "#449779");
telaCirculos(250, 0, 200, 0, -Math.PI, "#013D55", "#013D55");
var pattern = pintor.createPattern(tempCv, 'repeat');
return pattern;
function telaCirculos(x, y, r, angIn, angFim, corFundo, corLinha) {
pintor.fillStyle = corFundo;
pintor.strokeStyle = corLinha;
pintor.beginPath();
pintor.arc(x, y, r, angIn, angFim);
pintor.closePath();
pintor.stroke();
pintor.fill();
}
}
<canvas id='cv'></canvas>

How to rotate image in Canvas

I have built a canvas project with https://github.com/petalvlad/angular-canvas-ext
<canvas width="640" height="480" ng-show="activeateCanvas" ap-canvas src="src" image="image" zoomable="true" frame="frame" scale="scale" offset="offset"></canvas>
I am successfully able to zoom and pan the image using following code
scope.zoomIn = function() {
scope.scale *= 1.2;
}
scope.zoomOut = function() {
scope.scale /= 1.2;
}
Additionally I want to rotate the image. any help i can get with which library i can use and how can i do it inside angularjs.
You can rotate an image using context.rotate function in JavaScript.
Here is an example of how to do this:
var canvas = null;
var ctx = null;
var angleInDegrees = 0;
var image;
var timerid;
function imageLoaded() {
image = document.createElement("img");
canvas = document.getElementById("canvas");
ctx = canvas.getContext('2d');
image.onload = function() {
ctx.drawImage(image, canvas.width / 2 - image.width / 2, canvas.height / 2 - image.height / 2);
};
image.src = "https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcT7vR66BWT_HdVJpwxGJoGBJl5HYfiSKDrsYrzw7kqf2yP6sNyJtHdaAQ";
}
function drawRotated(degrees) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(degrees * Math.PI / 180);
ctx.drawImage(image, -image.width / 2, -image.height / 2);
ctx.restore();
}
<button onclick="imageLoaded();">Load Image</button>
<div>
<canvas id="canvas" width=360 height=360></canvas><br>
<button onclick="javascript:clearInterval(timerid);
timerid = setInterval(function() {
angleInDegrees += 2;
drawRotated(angleInDegrees);
}, 100);">
Rotate Left
</button>
<button onclick="javascript:clearInterval(timerid);
timerid = setInterval(function() {
angleInDegrees -= 2;
drawRotated(angleInDegrees);
}, 100);">
Rotate Right
</button>
</div>
With curtesy to this page!
Once you can get your hands on the canvas context:
// save the context's co-ordinate system before
// we screw with it
context.save();
// move the origin to 50, 35 (for example)
context.translate(50, 35);
// now move across and down half the
// width and height of the image (which is 128 x 128)
context.translate(64, 64);
// rotate around this point
context.rotate(0.5);
// then draw the image back and up
context.drawImage(logoImage, -64, -64);
// and restore the co-ordinate system to its default
// top left origin with no rotation
context.restore();
To do it in a single state change. The ctx transformation matrix has 6 parts. ctx.setTransform(a,b,c,d,e,f); (a,b) represent the x,y direction and scale the top of the image will be drawn along. (c,d) represent the x,y direction and scale the side of the image will be drawn along. (e,f) represent the x,y location the image will be draw.
The default matrix (identity matrix) is ctx.setTransform(1,0,0,1,0,0) draw the top in the direction (1,0) draw the side in the direction (0,1) and draw everything at x = 0, y = 0.
Reducing state changes improves the rendering speed. When its just a few images that are draw then it does not matter that much, but if you want to draw 1000+ images at 60 frames a second for a game you need to minimise state changes. You should also avoid using save and restore if you can.
The function draws an image rotated and scaled around its center point that will be at x,y. Scale less than 1 makes the images smaller, greater than one makes it bigger. ang is in radians with 0 having no rotation, Math.PI is 180deg and Math.PI*0.5 Math.PI*1.5 are 90 and 270deg respectively.
function drawImage(ctx, img, x, y, scale, ang){
var vx = Math.cos(ang) * scale; // create the vector along the image top
var vy = Math.sin(ang) * scale; //
// this provides us with a,b,c,d parts of the transform
// a = vx, b = vy, c = -vy, and d = vx.
// The vector (c,d) is perpendicular (90deg) to (a,b)
// now work out e and f
var imH = -(img.Height / 2); // get half the image height and width
var imW = -(img.Width / 2);
x += imW * vx + imH * -vy; // add the rotated offset by mutliplying
y += imW * vy + imH * vx; // width by the top vector (vx,vy) and height by
// the side vector (-vy,vx)
// set the transform
ctx.setTransform(vx, vy, -vy, vx, x, y);
// draw the image.
ctx.drawImage(img, 0, 0);
// if needed to restore the ctx state to default but should only
// do this if you don't repeatably call this function.
ctx.setTransform(1, 0, 0, 1, 0, 0); // restores the ctx state back to default
}

Categories

Resources