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
Related
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>
I've been experimenting with the HTML Canvas for weeks, and I use .arc to make circles, but when the circle is incomplete, it doesn't show like a piece of pie. Instead, it uses the shortest distance possible from one end to the other, and it fills the rest! Is there a way for it to show up as pie pieces?
Here's an example using .arc:
<html>
<head>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(100, 75, 50, 0, 2) //supposedly radians for 2
ctx.stroke();
ctx.fillStyle = "black";
ctx.fill();
</head>
<body>
<canvas id="canvas" width="1000" height="600"></canvas>
</body>
</html>
So what happens is that .arc() literally creates a angled path from your provided x and y coordinates. What you need to do is:
move your 'drawing pointer' where you want to place the circle
make a 'path' from that position, which is the center of your circle
draw the actual pie arc
go back to your circle to close that path.
The following code illustrates a working example of this:
const WIDTH = 100;
const HEIGHT = 100;
const RADIUS = 50;
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = WIDTH;
canvas.height = HEIGHT;
document.body.appendChild(canvas);
// identify center of your circle for learning purposes
// let's use the center of the canvas:
const [dx, dy] = [WIDTH / 2, HEIGHT / 2];
context.fillStyle = 'orange';
context.fillRect(dx, dy, 5, 5);
// clean / begin your paths
context.beginPath();
// move to (and start your path to) your position:
context.moveTo(dx, dy);
// create a circle using the dx,dy as the center of the circle:
const startAngleInRadians = 0;
const endAngleInRadians = Math.PI * 0.5;
const goAntiClockwise = false;
context.arc(dx, dy, RADIUS, startAngleInRadians, endAngleInRadians, goAntiClockwise);
// move back to your position (not required if you only draw 1 pie since your paths dont change):
context.moveTo(dx, dy);
// let's fill our pie with a pink color!:
context.fillStyle = '#FF00FF55';
context.fill();
To illustrate what is actually happening I made a simple canvas animation:
const WIDTH = 200;
const HEIGHT = 200;
const RADIUS = 50;
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const [dx, dy] = [0, 0];
canvas.width = WIDTH;
canvas.height = HEIGHT;
document.body.appendChild(canvas);
context.strokeStyle = 'red';
function sleep(timeout) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, timeout);
});
}
async function showHowItWorks() {
// move from arc center to the arc start position:
for (let i=0; i<RADIUS; i++) {
context.beginPath();
context.moveTo(dx, dy);
context.lineTo(dx + i, dy);
context.stroke();
await sleep(50);
}
// draw the arc from start position to arc end:
const angle = Math.PI * 0.5;
for (let i=0; i<angle; i+=0.05) {
context.beginPath();
context.arc(dx, dy, RADIUS, 0, i, false);
context.stroke();
await sleep(50);
}
// move from arc end back to the arc center:
for (let i=50; i>=0; i--) {
context.moveTo(dx, dy + RADIUS);
context.lineTo(dx, dy + i);
context.stroke();
await sleep(50);
}
}
showHowItWorks();
Of course, there are some flaws: I am not sure whether the .arc() command moves the 'drawing pointer' -like .moveTo()- or draws from the position to the arc -like .lineTo()-.
In the first case, the pointer jumps from your center position to the arc. In the second case it actually creates a line from your center towards the arc. But anyway, both ways will give you the same end result in most cases.
I want to create a canvas with a circle, and inside the circle should be a triangle. I know how to draw a simple circle (below), but how do I put in the triangle?
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
context.beginPath();
context.arc(75,100,55,0,2 * Math.PI);
context.stroke();
Add these lines before calling stroke
context.moveTo(75,75);
context.lineTo(100, 100);
context.lineTo(25,150);
context.lineTo(75,75);
It is coming out of the cirle a little bit, but you get the idea.
In order to draw a triangle inside a circle you need to calculate the positions of the vertices. Supposing that your triangle is equilateral the angle between vertices is 120degs or 2*Math.PI/3:
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
let cw = canvas.width = 300;// the width of the canvas
let ch = canvas.height = 300;// the height of the canvas
let c={// the circle: coords of the center and the radius
x:75,y:100,r:55
}
let angle = (2*Math.PI)/3;// the angle between vertices
points = [];// the vertices array
for(let i = 0; i < 3; i++){
let o = {}
o.x = c.x + c.r*Math.cos(i*angle);
o.y = c.y + c.r*Math.sin(i*angle);
points.push(o);
}
// draw the circle
context.beginPath();
context.arc(c.x,c.y,c.r,0,2 * Math.PI);
context.stroke();
// draw the triangle
context.beginPath();
context.moveTo(points[0].x,points[0].y);
for(let i = 1; i < points.length; i++){
context.lineTo(points[i].x,points[i].y);
}
context.closePath();
context.stroke();
canvas{border:1px solid}
<canvas id="myCanvas"></canvas>
I cannot create multiple clipping paths in canvas. With this code, if i=1, I get the clipping path working correctly. For i>1, I only see clipping if the paths are overlapping. Otherwise, nothing is drawn to the canvas.
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
for (var i = 0; i < 5; i++) {
ctx.beginPath();
var x = 25 + 25 * i; // x coordinate
var y = 75; // y coordinate
var radius = 20; // Arc radius
var startAngle = 0; // Starting point on circle
var endAngle = Math.PI * 2; // End point on circle
var anticlockwise = true; // clockwise or anticlockwise
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
ctx.clip();
}
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, 800, 150);
}
If it is not possible to have multiple clipping masks on the canvas, is there another compositing method that is the same as clipping mask?
If you wish to have multiple shapes in your clip area you need to define all the shapes then apply the clip. If you set the clip after adding each shape you end up clipping only inside the previous clip.
So move ctx.clip() to after the for loop, it need only be called once, and move the ctx.beginPath() to before the loop.
I have an image in my canvas and am trying to rotate it in an unusual way. I have my image rotating but on the wrong origin point.
I want to rotate the image in such a way that the ellipse is shown to be rotating from it's origin point rather than the image itself rotating. There for ultimately the ellipse would be stationary in the canvas and simply rotating.
This is my current code:
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
var angle;
var toRads = Math.PI/180;
var start = new Date().getTime();
var rotation = 15;
function draw(){
var x = canvas.width/2 - img.width/2;
var y = canvas.height/2 - img.height/2;
var deltaTime = new Date().getTime() - start;
start = new Date().getTime();
angle = (angle || 0) + (deltaTime/1000 * rotation);
if(angle > 360){angle = 0;}
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(x,y);
ctx.rotate(angle * toRads);
ctx.drawImage(img,20,20);
ctx.restore();
setTimeout(draw,1);
}
I was wondering if this can be done in 2D or is it only possible via 3D such as webGL stuff?
I created a jsfiddle of what I have got so far:
http://jsfiddle.net/hc0p5e06/
The order of the transformations are important. You are missing a translation to place the origin of the circle at the right point (assuming you want the center)
var w = img.width; // get the image width and height
var h = img.height;
ctx.translate(x,y);
ctx.rotate(angle * toRads);
ctx.drawImage(img,0,0,w,h,-w/2,-h/2,w,h); // draw the image translated half
// its size up and left in the new
// coordinate space