How to convert an Arc into a Line with HTML5 Canvas? - javascript

I would like to animate a circle into a line with the radius equaling the width, and I am wondering how I can do this with an Arc? Or perhaps there is a better way?
From
To
Here's my arc:
function drawStar(x,y,size,scale,opacity,ctx){
ctx.save();
ctx.beginPath();
ctx.arc(x,y,size+scale,0,size+scale * Math.PI,false);
ctx.globalAlpha = opacity
ctx.closePath();
setFillStyle('rgb(255,237,219)',ctx);
ctx.fill()
ctx.restore();
}
I tried using ctx.scale(n,1), however it does not keep the same radius(width) and it scales the collection of arcs as a whole (zoom in effect).

Use instead a wide line-width value with "round" lineCap and stroke():
var ctx = c.getContext("2d");
ctx.lineWidth = 50;
ctx.lineCap = "round";
ctx.moveTo(45 , 25);
ctx.lineTo(45.5, 25); // in IE11 this must be slightly offset
ctx.moveTo( 45, 100);
ctx.lineTo(150, 100);
ctx.stroke();
<canvas id=c></canvas>
Remember beginPath() for animation.

You can use Bezier Curves to 'transform' your arc.
There's some math involved in calculating the perfect ends of your stretched circle but I guessed and tweaked my numbers.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.moveTo(40, 20);
ctx.lineTo(100, 20);
ctx.bezierCurveTo(130, 20, 130, 60, 100, 60);
ctx.lineTo(40, 60);
ctx.bezierCurveTo(10, 60, 10, 20, 40, 20);
ctx.stroke();
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.moveTo(40, 80);
ctx.bezierCurveTo(68, 80, 68, 120, 40, 120);
ctx.bezierCurveTo(12, 120, 12, 80, 40, 80);
ctx.fill();
ctx.stroke();
<canvas id="myCanvas"></canvas>

You could draw the left and right halves of a circle using arc, then do a fillRect in between to connect them.
Edit: To elaborate on what I said earlier:
function init() {
let canvas = document.getElementById('myCanvas');
canvas.width = 400;
canvas.height = 400;
canvas.style.width = "400px";
canvas.style.height = "400px";
let ctx = canvas.getContext("2d");
function fillArc(ctx, cx, cy, r, startDeg, endDeg) {
ctx.beginPath();
ctx.arc(cx, cy, r, startDeg * Math.PI / 180, endDeg * Math.PI / 180);
ctx.fill();
}
function fillOval(ctx, cx, cy, r, sideLength, skipFirstArc) {
if (!skipFirstArc) {
fillArc(ctx, cx, cy, r, 90, 270);
}
ctx.fillRect(cx, cy - r, sideLength, r * 2);
fillArc(ctx, cx + sideLength, cy, r, 270, 90);
}
let sideLength = 0;
ctx.fillStyle = 'red';
function animateOval() {
if (sideLength === 100) {
ctx.clearRect(0, 0, 400, 400);
}
else {
fillOval(ctx, 30, 30, 25, sideLength, sideLength > 0);
}
++sideLength;
if (sideLength > 100) {
sideLength = 0;
}
}
setInterval(animateOval, 16);
}
Here's a Plunker with the above code running: http://plnkr.co/edit/vNqoUjPKg2lqC7JtYuEb?p=preview

Related

How to change order of text to appear on arc - context 2d, and stop fill style overwriting on the canvas 2d (using with chart.js)?

So I trying to get build an arc with text on it as rounded rectangle in context 2d but the text is going under it and is getting hidden, also, the fill style for arc is getting overridden by the fill style for text. Here is what I am getting and what I want:
Here is the js fiddle with the code : https://jsfiddle.net/abhishek_soni/vzs6dLy9/19/
Here is the same code :
Html code :
<canvas></canvas>
Js code :
var roundRect = function(ctx, x, y, w, h, r) {
var x2 = x + w;
var y2 = y + h;
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x2, y, x2, y2, r);
ctx.arcTo(x2, y2, x, y2, r);
ctx.arcTo(x, y2, x, y, r);
ctx.arcTo(x, y, x2, y, r);
};
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
context.save();
roundRect(context, 0, 0, 50, 60, 20);
context.fillStyle = 'red'; // This should work, but it doesn't
context.fill();
context.restore();
context.fillStyle = 'green';
context.fillText('Hey', 50, 80) // if I change this to 50 it hides behind the arc, which I don't want.
context.textAlign = "center";
context.fill();
context.save();
roundRect(context, 100, 100, 50, 90, 20);
context.fillStyle = 'red'; // This should work, but it doesn't
context.fill();
context.restore();
context.fillStyle = 'green';
context.fillText('Hey', 90, 100) // if I change this to 50 it hides behind the arc, which I don't want.
context.textAlign = "center";
context.fill();
context.save();
roundRect(context, 300, 100, 50, 120, 20);
context.fillStyle = 'red'; // This should work, but it doesn't
context.fill();
context.restore();
context.fillStyle = 'green';
context.fillText('Hey', 90, 130) // if I change this to 50 it hides behind the arc, which I don't want.
context.textAlign = "center";
context.fill();
The problem is you are calling fill() after you create the text which you don't need. That fill() is being applied to the shape drawn previously. The purpose of methods like fillText() and fillRect() are to allow you to draw without the need to call fill() after.
Don't call fill after the text and it works.
Next to align your text in the center of the shape I would calculate the center and place the text there and then use context.textAlign = 'center'; to center it.
Here's an example
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
canvas.width = innerWidth;
canvas.height = innerHeight;
let center = {}
var roundRect = function(ctx, x, y, w, h, r) {
var x2 = x + w;
var y2 = y + h;
center = {x: x + w/2, y: y + h/2}
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x2, y, x2, y2, r);
ctx.arcTo(x2, y2, x, y2, r);
ctx.arcTo(x, y2, x, y, r);
ctx.arcTo(x, y, x2, y, r);
ctx.closePath();
};
context.fillStyle = 'red';
roundRect(context, 0, 0, 50, 60, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.fillText('Hey', center.x, center.y);
context.textAlign = "center";
context.fillStyle = 'red';
roundRect(context, 100, 100, 50, 90, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.fillText('Hey', center.x, center.y)
context.textAlign = "center";
context.fillStyle = 'red';
roundRect(context, 300, 100, 50, 120, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.fillText('Hey', center.x, center.y);
context.textAlign = "center";
<canvas id="canvas"></canvas>
I'm also going to add an example using a class to do this. I prefer this over the other method. If you don't need different color shapes you can get rid of the color argument. And if you want different color text you can add one for that.
var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
canvas.width = innerWidth;
canvas.height = innerHeight;
class roundRect {
constructor(x, y, w, h, r, c) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.x2 = this.x + this.w;
this.y2 = this.y + this.h;
this.center = { x: x + w / 2, y: y + h / 2 };
this.r = r;
this.color = c;
}
drawShape() {
if (this.w < 2 * this.r) this.r = this.w / 2;
if (this.h < 2 * this.r) this.r = this.h / 2;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.moveTo(this.x + this.r, this.y);
ctx.arcTo(this.x2, this.y, this.x2, this.y2, this.r);
ctx.arcTo(this.x2, this.y2, this.x, this.y2, this.r);
ctx.arcTo(this.x, this.y2, this.x, this.y, this.r);
ctx.arcTo(this.x, this.y, this.x2, this.y, this.r);
ctx.closePath();
ctx.fill();
}
drawText() {
ctx.fillStyle = 'green';
ctx.textAlign = "center";
ctx.textBaseline = 'middle';
ctx.fillText("Hey", this.center.x, this.center.y);
}
}
let rect1 = new roundRect(0, 0, 50, 60, 20, 'red', 'Text')
let rect2 = new roundRect(60, 0, 50, 80, 20, 'lightblue', 'Text')
let rect3 = new roundRect(120, 0, 50, 100, 20, 'lightgreen', 'Text')
rect1.drawShape();
rect1.drawText();
rect2.drawShape();
rect2.drawText();
rect3.drawShape();
rect3.drawText();
<canvas id="canvas"></canvas>
EDIT:
To rotate your text use the following.
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
canvas.width = innerWidth;
canvas.height = innerHeight;
let center = {}
var roundRect = function(ctx, x, y, w, h, r) {
var x2 = x + w;
var y2 = y + h;
center = {x: x + w/2, y: y + h/2}
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x2, y, x2, y2, r);
ctx.arcTo(x2, y2, x, y2, r);
ctx.arcTo(x, y2, x, y, r);
ctx.arcTo(x, y, x2, y, r);
ctx.closePath();
};
context.fillStyle = 'red';
roundRect(context, 0, 0, 50, 60, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.fillText('Hey', center.x, center.y);
context.textAlign = "center";
context.fillStyle = 'red';
roundRect(context, 100, 100, 50, 90, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.save() //save before you transform something
context.translate(center.x, center.y) //use this to position
context.rotate(degToRad(90)) //rotate here using degrees
context.fillText('Hey', 0, 0) //draw this a (0, 0)
context.fillStyle = 'lightblue'; //delete this
context.fillRect(0, 0, canvas.width, canvas.height) //and this
context.restore() //restore when done with all objects you want affected
context.textAlign = "center";
context.fillStyle = 'red';
roundRect(context, 300, 100, 50, 120, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.fillText('Hey', center.x, center.y);
context.textAlign = "center";
function degToRad(deg) {
return deg * (Math.PI/180)
}
<canvas id="canvas"></canvas>

HTML5 canvas : counterclockwise arc with angle > 2 PI

I try to figure why angle > 2 PI does not give the same result when drawing an arc clockwise and counterclockwise.
Look at this code snippet, on the first line I draw "clockwise" 3 red arcs with a start angle of 0 and an end angle of PI, 2*PI and 3*PI.
Then I draw "counterclockwise" 3 blue arcs with same parameters.
The 3rd result bewilders me... Can anyone explain this to me ?
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// CLOCKWISE, angle = PI
ctx.beginPath();
ctx.arc(50, 50, 40, 0, Math.PI, false);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
// CLOCKWISE, angle = 2 PI
ctx.beginPath();
ctx.arc(150, 50, 40, 0, 2 * Math.PI, false);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
// CLOCKWISE, angle = 3 PI
ctx.beginPath();
ctx.arc(250, 50, 40, 0, 3 * Math.PI, false);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
// COUNTERCLOCKWISE, angle = PI
ctx.beginPath();
ctx.arc(50, 150, 40, 0, Math.PI, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
// COUNTERCLOCKWISE, angle = 2 PI
ctx.beginPath();
ctx.arc(150, 150, 40, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
// COUNTERCLOCKWISE, angle = 3 PI
ctx.beginPath();
ctx.arc(250, 150, 40, 0, 3 * Math.PI, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"/>
According to specs:
If anticlockwise is false and endAngle-startAngle is equal to or greater than 2π, or, if anticlockwise is true and startAngle-endAngle is equal to or greater than 2π, then the arc is the whole circumference of this ellipse, and the point at startAngle along this circle's circumference, measured in radians clockwise from the ellipse's semi-major axis, acts as both the start point and the end point.
Otherwise, the points at startAngle and endAngle along this circle's circumference, measured in radians clockwise from the ellipse's semi-major axis, are the start and end points respectively, and the arc is the path along the circumference of this ellipse from the start point to the end point, going anti-clockwise if anticlockwise is true, and clockwise otherwise. Since the points are on the ellipse, as opposed to being simply angles from zero, the arc can never cover an angle greater than 2π radians.
Put in maybe clearer pseudo-code:
if(
(anticlockwise === false && (endAngle - startAngle) >= 2π) ||
(anticlockwise === true && (startAngle - endAngle) >= 2π)
) {
arc_circumference = 2π;
}
else {
startAngle = startAngle % 2π;
endAngle = endAngle % 2π;
}
In your case, startAngle = 0, endAngle = 3π, anticlowkwise = true, if we run the above algorithm, we end up in the else case (0 - 3π < 2π) and endAngle is now (3π % 2π = 1π).
We could achieve the same output without the anticlockwise flag by swapping startAngle and endAngle:
var ctx = canvas.getContext("2d");
// COUNTERCLOCKWISE, angle = -3 PI (from OP)
ctx.beginPath();
ctx.arc(50, 150, 40, 0, 3 * Math.PI, true);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
// CLOCKWISE, angle = -3 PI
ctx.beginPath();
ctx.arc(50, 50, 40, 3 * Math.PI, 0);
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
<canvas id="canvas"></canvas>
When you go COUNTERCLOCKWISE, after 0 comes 2PI. You should try this instead:
// COUNTERCLOCKWISE, angle = 3 PI
ctx.beginPath();
ctx.arc(250, 150, 40, 2 * Math.PI, 0, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
UPDATE:
After OP's comment I've added an animated demo:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
let delta = 0;
function Draw(){
requestAnimationFrame(Draw)
delta+= .01;
ctx.clearRect(0,0,c.width,c.height)
// CLOCKWISE: animating the end point
ctx.beginPath();
ctx.arc(50, 50, 40, 0, delta, false);
ctx.closePath();
ctx.stroke();
// CONTERCLOCKWISE, animating the start point
ctx.beginPath();
ctx.arc(150, 50, 40, 0,-delta, true);
ctx.closePath();
ctx.stroke();
}
Draw()
<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"/>

How can I move just one of many shapes inside canvas by 50 pixels;

I have a project to do and I cannot do it until I understand how moving object inside canvas work.
I need to move one of the objects below by 50pixels to right.
Anyone willing to help me is greatly appreciated.
Meanwhile thank you very much in advance for your help or suggestions.
function canvasOneShape() {
//refers to the html canvasone id
var canvas = document.getElementById("canvasOne");
this.canvasOne.width = 945;
this.canvasOne.height = 650;
// draws the canvas in 2d
var ctx = canvas.getContext("2d");
// Set the fill colour to blue.
ctx.fillStyle = "blue"; //used like this instead of rgb due personal preference:)
// Create a filled rectangle at co-ordinates (10,10)
// with height and width set to 100.
ctx.fillRect(10, 10, 250, 330); //
// Here I draw the square
// Set the canvas up for drawing in 2D.
// Set the fill colour to blue.
ctx.fillStyle = "rgba(244, 244, 189,.5)";
ctx.fillRect(10, 50, 330, 250);
//draw my first circle
var midXone = canvas.width / 2; //x location
var midXtwo = canvas.height / 2; //y location
var radius = 60; //circle radius
ctx.beginPath();
ctx.arc(midXone, midXtwo, radius, 0, 2 * Math.PI, false);
ctx.fillStyle ="rgba(89, 192, 227,.4)";
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = '#003300';
ctx.stroke();
//draw the second circle
var midX = canvas.width / 2.35; //x location
var midY = canvas.height / 2.35; //y location
var radius = 50; //circle radius
ctx.beginPath();
ctx.arc(midX, midY, radius, 0, 2 * Math.PI, false);
ctx.fillStyle ="rgba(66, 244, 89,.4)";
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle ="rgba(255, 244, 9,.4)";
ctx.stroke();
//draw Square with circle inside
//square
ctx.fillStyle = "rgb(222, 33, 51)";
ctx.fillRect(550, 20, 300, 300);
//circle
ctx.beginPath();
ctx.arc(700, 170, 150, 0, 2 * Math.PI, false);
ctx.fillStyle ="rgba(66, 244, 89,.4)";
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = '#f44242';
ctx.stroke();
//The Pacman object
var radius = 100; //circle radius
var x = 100;
var y = 500;
ctx.beginPath();
ctx.arc(120, 500, radius, 1.85 * Math.PI, .15 * Math.PI, true);
//Draw mouth
ctx.lineTo(120, 500);
ctx.closePath();
ctx.fillStyle = "rgb(255, 255, 0)";
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = 'rgb(0,0,0)';
ctx.stroke();
//draw eye
ctx.beginPath();
ctx.arc(x + 40, y - 40, 10, 0 * Math.PI, 2 * Math.PI, true);
ctx.fillStyle = "rgb(0,0,0)";
ctx.fill();
}
there are no layers or objects on a canvas(it's just a single layer of pixels), so, if you draw something, you overwrite what's underneath it. to move something to the right, you will need to have a function to draw everything behind it, and then variables to store the location (x and y) of the objects you want to move.
then, to move it, you clear the canvas with
ctx.clearRect(0, 0,width of canvas, height height of canvas);,
call the background function to draw everything behind it again, and the redraw your object in a different location.

HTML 5 Canvas, rotate everything

I made a cylinder gauge, very similar to this one:
It is drawn using about 7 or so functions... mine is a little different. It is very fleixble in that I can set the colors, transparency, height, width, whether there is % text shown and a host of other options. But now I have a need for the same thing, but all rotated 90 deg so that I can set the height long and the width low to generate something more like this:
I found ctx.rotate, but no mater where it goes all the shapes fall apart.. ctx.save/restore appears to do nothing, I tried putting that in each shape drawing function. I tried modifying, for example, the drawOval function so that it would first rotate the canvas if horizontal was set to one; but it appeared to rotate it every single iteration, even with save/restore... so the top cylinder would rotate and the bottom would rotate twice or something. Very tough to tell what is really happening. What am I doing wrong? I don't want to duplicate all this code and spend hours customizing it, just to produce something I already have but turned horizontal. Erg! Help.
Option 1
To rotate everything just apply a transform to the element itself:
canvas.style.transform = "rotate(90deg)"; // or -90 depending on need
canvas.style.webkitTransform = "rotate(90deg)";
Option 2
Rotate context before drawing anything and before using any save(). Unlike the CSS version you will first need to translate to center, then rotate, and finally translate back.
You will need to make sure width and height of canvas is swapped before this is performed.
ctx.translate(ctx.canvas.width * 0.5, ctx.canvas.height * 0.5); // center
ctx.rotate(Math.PI * 0.5); // 90°
ctx.translate(-ctx.canvas.width * 0.5, -ctx.canvas.height * 0.5);
And of course, as an option 3, you can recalculate all your values to go along the other axis.
Look at the rotate function in this example. You want to do a translation to the point you want to rotate around.
example1();
example2();
function rotate(ctx, degrees, x, y, fn) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(degrees * (Math.PI / 180));
fn();
ctx.restore();
}
function rad(deg) {
return deg * (Math.PI / 180);
}
function example2() {
var can = document.getElementById("can2");
var ctx = can.getContext('2d');
var w = can.width;
var h = can.height;
function drawBattery() {
var percent = 60;
ctx.beginPath();
ctx.arc(35,50, 25,0,rad(360));
ctx.moveTo(35+percent+25,50);
ctx.arc(35+percent,50,25,0,rad(360));
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = "rgba(0,255,0,.5)";
ctx.arc(35,50,25,0,rad(360));
ctx.arc(35+percent,50,25,0,rad(360));
ctx.rect(35,25,percent,50);
ctx.fill();
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = "#666666";
ctx.moveTo(135,25);
ctx.arc(135,50,25, rad(270), rad(269.9999));
//ctx.moveTo(35,75);
ctx.arc(35,50,25,rad(270),rad(90), true);
ctx.lineTo(135,75);
ctx.stroke();
}
drawBattery();
can = document.getElementById("can3");
ctx = can.getContext('2d');
w = can.width;
h = can.height;
rotate(ctx, -90, 0, h, drawBattery);
}
function example1() {
var can = document.getElementById('can');
var ctx = can.getContext('2d');
var color1 = "#FFFFFF";
var color2 = "#FFFF00";
var color3 = "rgba(0,155,255,.5)"
var text = 0;
function fillBox() {
ctx.save();
ctx.fillStyle = color3;
ctx.fillRect(0, 0, can.width / 2, can.height);
ctx.restore();
}
function drawBox() {
ctx.save();
ctx.beginPath();
ctx.strokeStyle = ctx.fillStyle = color1;
ctx.rect(10, 10, 50, 180);
ctx.font = "30px Arial";
ctx.fillText(text, 25, 45);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = color2;
ctx.lineWidth = 10;
ctx.moveTo(10, 10);
ctx.lineTo(60, 10);
ctx.stroke();
ctx.restore();
}
fillBox();
rotate(ctx, 90, can.width, 0, fillBox);
text = "A";
drawBox();
color1 = "#00FFFF";
color2 = "#FF00FF";
text = "B";
rotate(ctx, 90, can.width, 0, drawBox);
centerRotatedBox()
function centerRotatedBox() {
ctx.translate(can.width / 2, can.height / 2);
for (var i = 0; i <= 90; i += 10) {
var radians = i * (Math.PI / 180);
ctx.save();
ctx.rotate(radians);
ctx.beginPath();
ctx.strokeStyle = "#333333";
ctx.rect(0, 0, 50, 50)
ctx.stroke();
ctx.restore();
}
}
}
#can,
#can2,
#can3 {
border: 1px solid #333333
}
<canvas id="can" width="200" height="200"></canvas>
<canvas id="can2" width="200" height="100"></canvas>
<canvas id="can3" width="100" height="200"></canvas>

How to correctly draw a circle with gaps in a canvas element?

I want to draw a circle. But, not really. I want a series of arcs (segments) that will imply a full circle. And it'll spin and it will be awesome. But I am obviously doing something wrong.
Please, check this fiddle: http://jsfiddle.net/utWdM/
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.lineWidth = 15;
ctx.strokeStyle = 'hsla(111, 56%, 50%, 0.67)';
ctx.beginPath();
ctx.arc(250, 250, 100, 0, Math.PI * 8 / 30);
ctx.moveTo(250, 250);
ctx.arc(250, 250, 100, Math.PI * 12 / 30, Math.PI * 20 / 30);
ctx.moveTo(250, 250);
ctx.arc(250, 250, 100, Math.PI * 24 / 30, Math.PI * 32 / 30);
ctx.moveTo(250, 250);
ctx.arc(250, 250, 100, Math.PI * 36 / 30, Math.PI * 44 / 30);
ctx.moveTo(250, 250);
ctx.arc(250, 250, 100, Math.PI * 48 / 30, Math.PI * 56 / 30);
ctx.stroke();
ctx.closePath();
}
}
draw();
I don't want the line from the center to the start of an arc (and the first one doesn't have it).
I've been studying basic drawing with canvas and hope understand what's going on and more someday, but I couldn't wait to know what's wrong in this situation.
Any help, highly appreciated.
If you want a little flexible solution, ie. if you want to change number of segments, size of each segments, then you can create a generic function such as this:
/**
* ctx = context
* x / y = center
* radius = of circle
* offset = rotation in angle (radians)
* segments = How many segments circle should be in
* size = size of each segment (of one segment) [0.0, 1.0]
*/
function dashedCircle(ctx, x, y, radius, offset, segments, size) {
var pi2 = 2 * Math.PI, /// cache 2*Math.PI = 360 degrees in rads
segs = pi2 / segments, /// calc. size of each segment in rads
len = segs * size, /// calc. length of segment in rads
i = 0,
ax, ay;
ctx.save();
ctx.translate(x, y);
ctx.rotate(offset); /// rotate canvas
ctx.translate(-x, -y);
for(; i < pi2; i += segs) {
ax = x + radius * Math.cos(i); /// calculate start position of arc
ay = y + radius * Math.sin(i);
ctx.moveTo(ax, ay); /// make sure to move to beginning of arc
ctx.arc(x, y, radius, i, i + len); /// draw arc
}
ctx.restore(); /// remove rotation
}
Then in you code you simply call:
ctx.beginPath();
dashedCircle(ctx, 250, 250, 100, 0, 5, 0.7);
ctx.lineWidth = 15;
ctx.strokeStyle = 'hsla(111, 56%, 50%, 0.67)';
ctx.stroke();
/// don't closePath here
Modified fiddle here
Using the offset parameter makes it a breeze to rotate it:
var offset = 0;
var step = 0.03;
var pi2 = 2 * Math.PI;
(function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
dashedCircle(ctx, 250, 250, 100, offset % pi2, 5, 0.7);
ctx.stroke();
offset += step;
requestAnimationFrame(loop);
})();
Rotating circle fiddle
The moveTo needs to be to the beginning of the arc not the centre. This is most easily achieved by translating to the centre of the circle as below. http://jsfiddle.net/utWdM/1
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.lineWidth = 15;
ctx.strokeStyle = 'hsla(111, 56%, 50%, 0.67)';
ctx.beginPath();
ctx.translate(250,250);
ctx.arc(0, 0, 100, 0, Math.PI * 8 / 30);
ctx.moveTo(100*Math.cos(Math.PI*12/30), 100*Math.sin(Math.PI*12/30));
ctx.arc(0, 0, 100, Math.PI * 12 / 30, Math.PI * 20 / 30);
ctx.moveTo(100*Math.cos(Math.PI*24/30), 100*Math.sin(Math.PI*24/30));
ctx.arc(0, 0, 100, Math.PI * 24 / 30, Math.PI * 32 / 30);
ctx.moveTo(100*Math.cos(Math.PI*36/30), 100*Math.sin(Math.PI*36/30));
ctx.arc(0, 0, 100, Math.PI * 36 / 30, Math.PI * 44 / 30);
ctx.moveTo(100*Math.cos(Math.PI*48/30), 100*Math.sin(Math.PI*48/30));
ctx.arc(0, 0, 100, Math.PI * 48 / 30, Math.PI * 56 / 30);
ctx.stroke();
ctx.closePath();
}
}
The easiest way to avoid complicated math is to call .beginPath() and .stroke(); for each segment. I know it's very repetitive but that how it would look like:
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.lineWidth = 15;
ctx.strokeStyle = 'hsla(111, 56%, 50%, 0.67)';
ctx.beginPath();
ctx.arc(250, 250, 100, 0, Math.PI * 8 / 30);
ctx.stroke();
ctx.beginPath();
ctx.arc(250, 250, 100, Math.PI * 12 / 30, Math.PI * 20 / 30);
ctx.stroke();
ctx.beginPath();
ctx.arc(250, 250, 100, Math.PI * 24 / 30, Math.PI * 32 / 30);
ctx.stroke();
ctx.beginPath();
ctx.arc(250, 250, 100, Math.PI * 36 / 30, Math.PI * 44 / 30);
ctx.stroke();
ctx.beginPath();
ctx.arc(250, 250, 100, Math.PI * 48 / 30, Math.PI * 56 / 30);
ctx.stroke();
}
}
draw();
And here's your fiddle: http://jsfiddle.net/utWdM/13/

Categories

Resources