Draw a circle with different colored section - html

So I want to draw a circle, filled blue with a black and red outline. The red part is determined by the look angle. The ctx variable holds the 2d context.
Relevant code:
ctx..lineWidth = 0.5
..fillStyle = "#0000AA"
..strokeStyle = "red";
ctx.beginPath();
ctx.arc(pos.x, pos.y, radius, look - PI / 6, look + PI / 6);
ctx..fill()
..closePath()
..stroke()
..beginPath();
ctx.strokeStyle = "black";
ctx.arc(pos.x, pos.y, radius, look + PI / 6, look - PI / 6);
ctx..fill()
..closePath()
..stroke();
This, however draws an additional red line inside the circle, that I don't want. How can I get rid of this line?

Remove the closePath when drawing the red line.
closePath will draw a line connecting the endpoints of your red arc (not what you want).

Related

How can I draw a line with lineTo in HTML5 that has angles to match the Unit Circle?

If I have this,
var canvas = document.getElementById("my-canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = '#f00';
ctx.beginPath();
ctx.moveTo(25, 25);
ctx.lineTo(150, 25);
ctx.rotate(Math.PI*7/4); //315 degrees
ctx.lineTo(150,90);
ctx.stroke();
It does not draw the line (the ctx.lineTo(150,90);) at the angle I thought it would which is from the end of the first lineTo at 25,25 to a 150,90 at a 45 degree angle. If I use -Math.PI*7/4 I get what looks like a 45 degree angle, but it points the wrong way. Math.PI*5/4 goes the wrong way or rotation.
My question asks about the Unit Circle. I don't really need them all at once, just the ability to know how to draw them if I need them.
The canvas is rotating this way:
Consider the following example, without the y offset:
Given by the code:
var canvas = document.getElementById("my-canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = '#f00';
ctx.beginPath();
ctx.moveTo(25, 0);
ctx.lineTo(250, 0);
ctx.rotate(45*Math.PI/180); //45 degrees
ctx.lineTo(150,0);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0,0);
ctx.strokeStyle = '#ff0000';
ctx.lineTo(350,0);
ctx.font = "30px Arial";
ctx.fillText("New x axis",100,50);
ctx.stroke();
The red line is the new x axis, and the black line touches it exactly at a distance of 150 from the origin.
Now overlaying the same red line with your coordinates (and rotating by 45 degrees, so that it doesn't go off screen), we get
The black line ends at a point which is indeed (150,90), however at the new coordinate system. JSFiddle here
If you want the angle between the two line segments to be exactly what you pass in rotate(...), then keep drawing "horizontal" lines, but translate the origin to the end of each line segment, so you rotate about this point, not the top-left corner, using ctx.translate(<current point>). For example: https://jsfiddle.net/Douma37/65z1p38z/ (this goes off canvas, but has minimal interferance with your code).
Hope that helps!

How to fill custom shape has been draw in Canvas?

Im try to drew custom shape, but since I use moveTo ..I cant be filled it, so That my question is there's any way can may be determined points on screen to fill shape? or to do that I most use or drew another real shape in the same block as layer ...
Look at my example here to drew a simple shape:
to I can fill image with blue color Im drew a Fill rectangle, so That is is a true way?
Code for shape for before fill:
var canvas3 = document.getElementById('canvas3');
var c3 = canvas3.getContext('2d');
c3.fillStyle = 'green';
c3.beginPath();
c3.moveTo(10,30);
c3.lineTo(200,30);
c3.moveTo(10,80);
c3.lineTo(200,80);
c3.moveTo(10,30);
c3.lineTo(10,180);
c3.moveTo(200,30);
c3.lineTo(200,180);
//c3.closePath();
c3.fill();
c3.lineWidth = 5;
c3.strokeStyle = 'orange';
c3.stroke();
Code for shape after fill:
var canvas3 = document.getElementById('canvas3');
var c3 = canvas3.getContext('2d');
c3.fillStyle = 'blue';
c3.beginPath();
c3.moveTo(10,30);
c3.fillRect(10,30,190,60);
c3.moveTo(10,30);
c3.lineTo(10,180);
c3.moveTo(10,90);
c3.lineTo(200,90);
c3.moveTo(200,30);
c3.lineTo(200,180);
c3.moveTo(10,30);
c3.lineTo(200,30);
//c3.closePath();
c3.fill();
c3.lineWidth = 5;
c3.strokeStyle = 'orange';
c3.stroke();
and finally which is a best way to I can drew shapes like this?
Note: Im new on html5 canvas and I read from this online book.
is there any way can may be determined points on screen to fill
shape? or to do that I most use or drew another real shape in the same
block as layer
Just draw a shape in the same place. Fill first then stroke afterwards. A little planning may be required with canvas as to in which order to draw things.
You can define objects to hold the geometrical data if you plan to redraw often or move them around. This will certainly simplify the objective later on.
which is a best way to I can drew shapes like this?
In my opinion this code can be drawn much simpler and in fewer lines of codes. There is no need to break up a shape in several parts as in that code if you can draw a shape using a simple method for it. In this case four lines can be replaced with one rectangle.
Knowing how these shapes are drawn internally also helps so we can take advantage of the path a rect() leaves, i.e. closing in upper-left corner so we can continue from there.
For example:
var c3 = c.getContext("2d");
// fill blue box first as it will be at the bottom
c3.rect(10, 30, 190, 50); // x, y, width, height
c3.fillStyle = 'blue';
c3.fill();
// orange stroke
// we know rect() will close at upper-left corner so continue from there with left leg
c3.lineTo(10, 180);
c3.moveTo(200, 80); // create second leg at right separately
c3.lineTo(200, 180);
c3.strokeStyle = "orange";
c3.lineWidth = 5;
c3.lineJoin = c3.lineCap = "round";
c3.stroke();
<canvas id=c height=180></canvas>
An alternative approach would be to fill then build the line path:
var c3 = c.getContext("2d");
c3.fillStyle = 'blue';
c3.fillRect(10, 30, 190, 50); // fillRect does not add to path
// orange stroke
c3.moveTo(10, 180); // create "housing" starting at bottom-left corner
c3.lineTo(10, 30); // upper-left
c3.lineTo(200, 30); // upper-right
c3.lineTo(200, 180); // bottom-right
c3.moveTo(10, 80); // add cross-bar
c3.lineTo(200, 80);
c3.strokeStyle = "orange";
c3.lineWidth = 5;
c3.lineJoin = c3.lineCap = "round";
c3.stroke();
<canvas id=c height=180></canvas>

Canvas inner stroke

I studied strokeStyle a bit but I cant find how to control the position of the stroke from inner/center/outer. It seems all stroke is outside the rectangle I draw. Is there anyway make the stroke be inner? (or even centered on the rectangle bounds)?
Thanks
Hope this helps!
Instead of doing:
ctx.fill();
ctx.stroke();
DO:
ctx.save();
ctx.clip();
ctx.lineWidth *= 2;
ctx.fill();
ctx.stroke();
ctx.restore();
Edit
For me I believe this works because the clip method removes any fill and stroke around the already present fill area, meaning the only place the stroke can go is on the inside because else it would be clipped off.
The default stroke do use centered stroke but there is unfortunately no parameter to control the alignment of the stroke so you would either have to calculate an offset value for the rectangle's position and size, or combine two rectangles and use for example the fill-rule evenodd:
var ctx = c.getContext("2d");
// default centered
ctx.lineWidth = 10;
ctx.strokeRect(10, 10, 100, 100);
ctx.lineWidth = 1;
ctx.strokeStyle = "red";
ctx.strokeRect(10, 10, 100, 100); // show main path
// inner
ctx.rect(150, 10, 100, 100);
ctx.rect(150+10, 10+10, 100-20, 100-20); // offset position and size
ctx.fill("evenodd"); // !important
ctx.strokeRect(150, 10, 100, 100);
<canvas id=c></canvas>
This answer "Draw outer and inner border around any canvas shape" shows how to use masking and compositing to precisely control the offset, both inwards and outwards of a stroke without the need to manipulate paths. It can be used for any canvas path no matter how complex.

Gradient stroke along canvas arc disappearing when context translates to center

I am very new to canvas drawings. I am trying to apply gradient along the arc of a circle. I can get it to appear fine when I draw the arc with center offset from the context coordinates. Lets say centerX and centerY denote the center of the canvas. I can get a gradient arc using context.arc(centerX, centerY, radius, ......).
Working example: http://jsfiddle.net/m5Pmb/
But when I try to draw the arc around the context coordinates, the gradient disappears. For example, I take the above working jsfiddle example, do context.translate(centerX, centerY), then do context.arc(0,0,radius,......), the resulting arc does not have any gradient on it.
Example here: http://jsfiddle.net/N6NMB/
In my case, I need to spin the resulting circle around its axis using context.rotate(), so I must translate to the center and draw circle around (0,0). But I cant understand why the gradient disappears when trying to draw arc around context's (0,0) point. Any insight would be really helpful.
Since you are translating the context centerX and centerY are not where you think anymore.
When you translate the context you are saying you want that x and y to be the new 0,0. So now your 0,0 is in the center of the canvas, so centerX and centerY are offset by themselves putting them further away from the center.
One method you can use is the following
var grad = context.createLinearGradient(
-radius,
radius / 2,
radius,
radius / 2
);
live Demo
The above works because its called after you translate the context, so centerX and centerY (like I said previously) are 0,0 meaning they don't need to be referenced in that function.
The gradients that you create will be painted with the actual transform in use.
So if you are not using any transform, no need to wonder anything : define your gradient where you're about to draw, and you'll be fine.
If you are using transform, you must think of the coordinates of the gradient as relative to the point/angle/scale when you'll use them.
To explain further, i modified your example and used a radial gradient.
I created a normalized gradient : it is defined in between 0.0 and 1.0 radius, meaning it will have its x and y in [-1; 1].
var eyeGrad = context.createRadialGradient(0, 0, 0, 0, 0, 1.0);
Then to use the gradient i must :
1) translate to be in the center of the figure i want to draw.
2) scale to have normalized coordinates.
function drawEye(x, y, r) {
context.save();
//translate context to center
context.translate(x, y);
// scale to radius
context.scale(r, r);
context.beginPath();
// draw an arc with radius of 1
context.arc(0, 0, 1, 0, 2 * Math.PI, false);
context.fillStyle = eyeGrad;
context.fill();
context.restore();
}
fiddle is here :
http://jsfiddle.net/gamealchemist/N6NMB/3/
Result for :
drawEye(100, 100, 40);
drawEye(250, 120, 20);

How to draw segment of a donut with HTML5 canvas?

As the title states. Is this possible?
Edit: When i say doughnut I mean a top, 2D view
Is the only option to draw a segment of a circle, then draw a segment of a smaller circle with the same origin and smaller radius over the top, with the colour of the background? That would be crap if so :(
You do it by making a single path with two arcs.
You draw one circle clockwise, then draw a second circle going counter-clockwise. I won't go into the detail of it, but the way paths are constructed knows to take this as a reason to un-fill that part of the path. For more detail of what its doing you can this wiki article.
The same would work if you were drawing a "framed" rectangle. You draw a box one way (clockwise), then draw the inner box the other way (counter-clockwise) to get the effect.
Here's the code for a doughnut:
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
// Pay attention to my last argument!
//ctx.arc(x,y,radius,startAngle,endAngle, anticlockwise);
ctx.beginPath()
ctx.arc(100,100,100,0,Math.PI*2, false); // outer (filled)
ctx.arc(100,100,55,0,Math.PI*2, true); // inner (unfills it)
ctx.fill();
Example:
http://jsfiddle.net/Hnw6a/
Drawing only a "segment" of it can be done by making the path smaller (you might need to use beziers instead of arc), or by using a clipping region. It really depends on how exactly you want a "segment"
Here's one example: http://jsfiddle.net/Hnw6a/8/
// half doughnut
ctx.beginPath()
ctx.arc(100,100,100,0,Math.PI, false); // outer (filled)
ctx.arc(100,100,55,Math.PI,Math.PI*2, true); // outer (unfills it)
ctx.fill();
You can make a 'top view doughnut' (circle with hollow center) by stroking an arc. You can see an example of this here: http://phrogz.net/tmp/connections.html
The circles (with nib) are drawn by lines 239-245:
ctx.lineWidth = half*0.2; // set a nice fat line width
var r = half*0.65; // calculate the radius
ctx.arc(0,0,r,0,Math.PI*2,false); // create the circle part of the path
// ... some commands for the nib
ctx.stroke(); // actually draw the path
Yes, I understand how old this question is :)
Here are my two cents:
(function(){
var annulus = function(centerX, centerY,
innerRadius, outerRadius,
startAngle, endAngle,
anticlockwise) {
var th1 = startAngle*Math.PI/180;
var th2 = endAngle*Math.PI/180;
var startOfOuterArcX = outerRadius*Math.cos(th2) + centerX;
var startOfOuterArcY = outerRadius*Math.sin(th2) + centerY;
this.beginPath();
this.arc(centerX, centerY, innerRadius, th1, th2, anticlockwise);
this.lineTo(startOfOuterArcX, startOfOuterArcY);
this.arc(centerX, centerY, outerRadius, th2, th1, !anticlockwise);
this.closePath();
}
CanvasRenderingContext2D.prototype.annulus = annulus;
})();
Which will add a function "annulus()" similar to "arc()" in the CanvasRenderingContext2D prototype. Making the closed path comes in handy if you want to check for point inclusion.
With this function, you could do something like:
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");
ctx.annulus(0, 0, 100, 200, 15, 45);
ctx.fill();
Or check this out: https://jsfiddle.net/rj2r0k1z/10/
Thanks!
With WebGL (one of the contexts of the HTML5 canvas) that is possible. There are even some JS libraries for browsers that don't support/implement it yet - check out these links:
http://sixrevisions.com/web-development/how-to-create-an-html5-3d-engine/
http://slides.html5rocks.com/#landing-slide
http://sebleedelisle.com/2009/09/simple-3d-in-html5-canvas/
http://www.khronos.org/webgl/
http://webdesign.about.com/od/html5tutorials/f/is-there-a-3d-context-for-html5-canvas.htm
http://code.google.com/p/html-gl/
Given the requirements, what #SimonSarris says satisfies the problem. But lets say you're like me and you instead want to "clear" a part of a shape that may be partially outside the bounds of the shape you're clearing. If you have that requirement, his solution won't get you want you want. It'll look like the "xor" in the image below.
The solution is to use context.globalCompositeOperation = 'destination-out' The blue is the first shape and the red is the second shape. As you can see, destination-out removes the section from the first shape. Here's some example code:
explosionCanvasCtx.fillStyle = "red"
drawCircle(explosionCanvasCtx, projectile.radius, projectile.radius, projectile.radius)
explosionCanvasCtx.fill()
explosionCanvasCtx.globalCompositeOperation = 'destination-out' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
drawCircle(explosionCanvasCtx, projectile.radius + 20, projectile.radius, projectile.radius)
explosionCanvasCtx.fill()
Here's the potential problem with this: The second fill() will clear everything underneath it, including the background. Sometimes you'll want to only clear the first shape but you still want to see the layers that are underneath it.
The solution to that is to draw this on a temporary canvas and then drawImage to draw the temporary canvas onto your main canvas. The code will look like this:
diameter = projectile.radius * 2
console.log "<canvas width='" + diameter + "' height='" + diameter + "'></canvas>"
explosionCanvas = $("<canvas width='" + diameter + "' height='" + diameter + "'></canvas>")
explosionCanvasCtx = explosionCanvas[0].getContext("2d")
explosionCanvasCtx.fillStyle = "red"
drawCircle(explosionCanvasCtx, projectile.radius, projectile.radius, projectile.radius)
explosionCanvasCtx.fill()
explosionCanvasCtx.globalCompositeOperation = 'destination-out' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
durationPercent = (projectile.startDuration - projectile.duration) / projectile.startDuration
drawCircle(explosionCanvasCtx, projectile.radius + 20, projectile.radius, projectile.radius)
explosionCanvasCtx.fill()
explosionCanvasCtx.globalCompositeOperation = 'source-over' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
ctx.drawImage(explosionCanvas[0], projectile.pos.x - projectile.radius, projectile.pos.y - projectile.radius) #center
Adapting/simplifying #Simon Sarris's answer to easily work with any angle gives the below:
To create an arc segment you draw an outer arc (of n radians) in one direction and then an opposite arc (of the same number of radians) at a smaller radius and fill in the resulting area.
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
var angle = (Math.PI*2)/8;
var outer_arc_radius = 100;
var inner_arc_radius = 50.;
ctx.beginPath()
//ctx.arc(x,y,radius,startAngle,endAngle, anticlockwise);
ctx.arc(100,100,outer_arc_radius,0,angle, false); // outer (filled)
// the tip of the "pen is now at 0,100
ctx.arc(100,100,inner_arc_radius,angle,0, true); // outer (unfills it)
ctx.fill();
<canvas id="canvas1" width="200" height="200"></canvas>