HTML5 canvas: single stroke around combined regions - html

HTML5 canvas: I'm looking for a way to draw a single stroke around a combined path.
For example if I have two overlapping circles I don't want to have two overlapping circle strokes, but one single stroke around the combined region of both circles..
Any chance for that?

It can be done by using globalCompositeOperation. There are various ways you can draw the shapes them selves but here is one approach that results in this (for the two rectangle circles in the demo):
Step 1: setup the normal canvas
Step 2: setup an off-screen canvas
Update Not sure how I could miss the obvious, but you can of course just stroke the circles first, then punch a whole with composite mode and a fill - much faster (I guess I had images on my mind when I came up with the offset redraw).
The reason for off-screen canvas is if you have something in the background already on the main canvas. This will be deleted otherwise where we punch the hole. If nothing is there there is no problem drawing this to a single canvas - updated code:
/// some regions
var rect = [ [20, 20, 200, 200], [100, 100, 200,200] ],
/// ox = off-screen context
ox.strokeStyle = '#fff';
ox.lineWidth = 3 * 2; /// x2 as half will be gone when we punch hole
/// stroke outlines
for(; r = rect[i]; i++) {
o = r[2] * 0.5;
ox.beginPath();
ox.arc(r[0] + o, r[1] + o, o, 0, 2 * Math.PI);
ox.stroke();
}
/// punch hole with composite mode and fill
ox.globalCompositeOperation = 'destination-out';
for(i = 0; r = rect[i]; i++) {
o = r[2] * 0.5;
ox.beginPath();
ox.arc(r[0] + o, r[1] + o, o, 0, 2 * Math.PI);
ox.fill();
}
/// draw result to main canvas
/// ctx = main context, ocanvas = off-screen canvas
ctx.drawImage(ocanvas, 0, 0);
(Animated) online demo using this optimized version
I'll leave the old code as it can be used for images that can't be stroked -
Now draw the shapes filled to the off-screen canvas. Draw in the color you want the outline to be in.
/// some regions
var rect = [ [20, 20, 200, 200], [100, 100, 200,200] ],
/// ox = off-screen canvas
ox.fillStyle = '#fff';
/// draw the array with circes
for(; r = rect[i]; i++) {
var o = r[2] * 0.5;
ox.beginPath(); //use this here - arcs are currently buggy
ox.arc(r[0] + o, r[1] + o, o, 0, 2 * Math.PI);
ox.fill(); //.. and here
}
Now draw the cached image of the shapes back to main canvas. the shapes must be drawn with a slight offset in each direction - this step will create the outline:
/// ctx = main context, ocanvas = off-screen canvas
ctx.drawImage(ocanvas, -1, -1);
ctx.drawImage(ocanvas, 1, -1);
ctx.drawImage(ocanvas, 1, -1);
ctx.drawImage(ocanvas, 1, 1);
ctx.drawImage(ocanvas, -1, 1);
ctx.drawImage(ocanvas, 1, 1);
ctx.drawImage(ocanvas, -1, -1);
ctx.drawImage(ocanvas, -1, 1);
And finally we punch a "hole" in the filled shape to make it transparent with an outline using globalCompositeOperation + a final draw in 0 offset position :
ctx.globalCompositeOperation = 'destination-out';
ctx.drawImage(ocanvas, 0, 0);
ONLINE DEMO
To make the border thicker just increase the offset when you draw back the shapes to main canvas.

That's my current solution. Doesn't need a second canvas and is easier to achieve. It still uses the Ken's idea to use globalCompositeOperation:
context.lineWidth = 2;
context.stroke();
var prev = context.globalCompositeOperation;
context.globalCompositeOperation = "destination-out";
context.fill();
context.globalCompositeOperation = prev;

Related

HTML5 canvas rotation not centered at origin [duplicate]

Hi I want to rotate this shape around its center when I move my mouse, but currently it's rotating around (0, 0). How to change my code?
Source code (also see jsfiddle):
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
class Circle {
constructor(options) {
this.cx = options.x;
this.cy = options.y;
this.radius = options.radius;
this.color = options.color;
this.angle = 0;
this.toAngle = this.angle;
this.binding();
}
binding() {
const self = this;
window.addEventListener('mousemove', (e) => {
self.update(e.clientX, e.clientY);
});
}
update(nx, ny) {
this.toAngle = Math.atan2(ny - this.cy, nx - this.cx);
}
render() {
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.beginPath();
ctx.lineWidth = 1;
if (this.toAngle !== this.angle) {
ctx.rotate(this.toAngle - this.angle);
}
ctx.strokeStyle = this.color;
ctx.arc(this.cx, this.cy, this.radius, 0, Math.PI * 2);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.fillStyle = 'black';
ctx.fillRect(this.cx - this.radius / 4, this.cy - this.radius / 4, 20, 20);
ctx.closePath();
ctx.restore();
}
}
var rotatingCircle = new Circle({
x: 150,
y: 100,
radius: 40,
color: 'black'
});
function animate() {
rotatingCircle.render();
requestAnimationFrame(animate);
}
animate();
All good answers, well frustratingly no... they fail to mention that the solution only works if the current transform is at it default. They fail to mention how to get back to the default state and save and restore states.
To get the default transformation state
ctx.setTransform(1,0,0,1,0,0);
To save and restore all states
ctx.save();
ctx.transform(10,0,0,2,200,100); // set some transform state
ctx.globalAlpha = 0.4;
ctx.restore(); // each save must be followed by a restore at some point
and they can be nested
ctx.save(); // save default state
ctx.globalAlpha = 0.4;
ctx.save(); // save state with alpha = 0.4
ctx.transform(10,0,0,2,200,100); // set some transform state
ctx.restore(); // restore to alpha at 0.4
ctx.restore(); // restore to default.
setTransform completely replaces the current transformation. while transform, scale, rotate, translate, multiply the existing transform with the appropriate transform. This is handy if you have an object attached to another, and want the transformation of the first to apply to the second, and additional transforms to the second but not to the first.
ctx.rotate(Math.PI /2); // Rotates everything 90 clockwise
ctx.rotate(Math.PI /2); // Rotates everything another 90 clockwise so that
// everything is 180 from default
ctx.translate(1,1); // move diagonally down by 1. Origin is now at 1,1
ctx.translate(1,1); // move diagonally down by 1. Origin is now at 2,2
ctx.translate(1,1); // move diagonally down by 1. Origin is now at 3,3
ctx.translate(1,1); // move diagonally down by 1. Origin is now at 4,4
ctx.scale(2,2); // scale by 2 everything twice as big
ctx.scale(2,2); // scale by 2 everything four times as big
And an alternative that does not require the default transform state of ctx
// scaleX, scaleY are scales along axis x,y
// posX, posY is position of center point
// rotate is in radians clockwise with 0 representing the x axis across the screen
// image is an image to draw.
ctx.setTransform(scaleX,0,0,scaleY, posX, posY);
ctx.rotate(rotate);
ctx.drawImage(image,-image.width / 2, -image.height / 2);
Or if not a image but a object
ctx.setTransform(scaleX,0,0,scaleY, posX, posY);
ctx.rotate(rotate);
ctx.translate(-object.width / 2, -object.height / 2);
You need to:
first translate to the point of rotation (pivot)
then rotate
then either:
A: draw in at (0,0) using (-width/2, -height/2) as relative coordinate (for centered drawings)
B: translate back and use the object's absolute position and subtract relative coordinates for centered drawing
Modified code:
ctx.beginPath();
ctx.lineWidth = 1;
ctx.translate(this.cx, this.cy); // translate to pivot
if (this.toAngle !== this.angle) {
ctx.rotate(this.toAngle - this.angle);
}
ctx.strokeStyle = this.color;
ctx.arc(0, 0, this.radius, 0, Math.PI * 2); // render at pivot
ctx.closePath(); // must come before stroke() btw.
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = 'black';
ctx.fillRect(-this.radius / 4, -this.radius / 4, 20, 20); // render at pivot
Modified Fiddle
Bonus tip: you're currently using save()/restore() calls to maintain the transformation matrix. Another way could be to set the matrix using absolute values initially replacing the save()/restore() - so instead of the first translate():
ctx.setTranform(1,0,0,1,this.cx, this.cy); // translate to pivot
You can also set things like styles on an individual basis for each. Regardless, it doesn't change the core solution though.
You have to first translate to the circle centre, make the rotation and then translate back
Do this before rendering the circle and the square
ctx.translate(this.cx, this.cy);
ctx.rotate(this.toAngle - this.angle);
ctx.translate(-this.cx, -this.cy);
jsfiddle below:
https://jsfiddle.net/1st8Lbu8/2/

How can I create an irregularly shaped HTML Canvas using FabricJS?

I've been attempting to create a triangle shaped canvas for a day or so now and I'm having no luck. The canvas is always square/rectangle. I'm using FabricJS but I can also manipulate the canvas directly if that's an option.
I've attempted using .clipTo(ctx) to clip the canvas as described here: Canvas in different shapes with fabricjs plugin
I've also attempted manipulating the canvas directly as I saw here: https://www.html5canvastutorials.com/tutorials/html5-canvas-custom-shapes/
What I'm trying to accomplish is for a user to drag-drop images onto a triangle shaped canvas so there's no "bleed" of the image outside the triangle shape. I accomplished this easily with a rectangle but I can't figure out how to change the canvas shape. OR if anyone has a "trick" solution that would look like the canvas was a triangle but under the hood remain a square, that would work as well.
Using pure API's
I don't use fabric (especially if its just for simple image manipulation) so you will have to locate the appropriate fabric functions to match this answer.
The canvas is always 4 sided. 2D and 3D transforms can change the shape but that also changes the shape of the contained pixels.
You have 2 simple options. There are other ways to do this but they are complex and have compatibility issues.
Visual only
Masking
To get the appearance of irregular shaped canvas you can use a mask (second canvas has mask). Draw the content to the main canvas and then mask that canvas with the mask.
Use the property CanvasRenderingContext2D.globalCompositeOperation to define how the mask is applied.
eg
function createTriangleMask(w, h) {
const mask = document.createElement("canvas");
mask.width = w;
mask.height = h;
mask.ctx = mask.getContext("2d");
mask.ctx.beginPath();
mask.ctx.lineTo(w / 2, 0);
mask.ctx.lineTo(w , h);
mask.ctx.lineTo(0 , h);
mask.ctx.fill();
return mask;
}
const mask = createTriangleMask(ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(myImg, 0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(mask, 0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.globalCompositeOperation = "source-over";
Using 2D clip
Or you can use the 2D API CanvasRenderingContext2D.clip to create a clip region and draw the content while the clip is active. Don't forget to pop the 2D state when done with the clip,
function triangleClip(ctx, w, h) {
ctx.save();
ctx.beginPath();
mask.ctx.lineTo(w / 2, 0);
mask.ctx.lineTo(w , h);
mask.ctx.lineTo(0 , h);
ctx.clip();
}
triangleClip(ctx, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(myImg, 0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.restore(); // Turn off clip. Must do before calling triangle clip again.
Still rectangular!
This has not changed the canvas shape. It is still a rectangle, just that some pixels are transparent. The DOM still sees a rectangle and user interactions with the canvas will still use the whole rectangular canvas.
CSS clip-path
You can use the style property clip-path to define the shape of a element. This will clip the elements visual content and the elements interactive area. Effectively turning any applicable element to an irregular shaped element.
Using JS Declarative
canvas.style.clipPath = "polygon(50% 0, 100% 100%, 0% 100%)"
Using JS
function clipElement(el, shape) {
var rule = "polygon(", i = 0, comma = "";
while (i < shape.length) {
rule += comma + shape[i++] + "% " + shape[i++] + "%";
comma = ",";
}
el.style.clipPath = rule + ")";
}
clipElement(canvas, [50, 0, 100, 100, 0, 100]);
Using CSS rule
canvas {
clip-path: polygon(50% 0, 100% 100%, 0% 100%);
}
With the clipped path in place the canvas will obey its shape via UI
canvas.style.cursor = "pointer"; // Pointer change only inside clipped area
canvas.title = "foo"; // appears only when over clipped area
canvas.addEventListener("mouseover", () => console.log("foo")); // fires when crossing
// clip boundary
Demo
Creates an animated clip via JS on the canvas element with content rendered once.
There are limitations
Note that the CSS defined background color (yellow) and shadow are also clipped. Many other visual properties will also be clipped.
Note that JS animation does not update UI events if there are no intervening user iteration.
The animation can also be achieved via CSS.
Compatibility with fabric is unknown to me, check their documentation.
var clearConsole = 0;
const s = 2 ** 0.5 * 0.25, clipPath = [0.5, 0, 0.5 + s, 0.5 + s, 0.5 - s, 0.5 + s], img = new Image;
img.src = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1";
img.addEventListener("load",() => canvas.getContext("2d").drawImage(img, 0, 0, 300, 300), {once: true});
requestAnimationFrame(animateLoop);
function clipRotate(el, ang, scale, path) {
const dx = Math.cos(ang) * scale;
const dy = Math.sin(ang) * scale;
var clip = "polygon(", i = 0, comma = "";
while (i < path.length) {
const x = path[i++] - 0.5;
const y = path[i++] - 0.5;
clip += comma;
clip += ((x * dx - y * dy + 0.5) * 100) + "% ";
clip += ((x * dy + y * dx + 0.5) * 100) + "%";
comma = ",";
}
el.style.clipPath = clip + ")";
}
function animateLoop(time) {
clipRotate(canvas, time / 1000 * Math.PI, 0.9, clipPath);
requestAnimationFrame(animateLoop);
if (clearConsole) {
clearConsole --;
!clearConsole && console.clear();
}
}
canvas.addEventListener("pointerenter", () => (clearConsole = 30, console.log("Pointer over")));
body {
background-color: #49C;
}
canvas {
cursor: pointer;
background-color: yellow;
box-shadow: 12px 12px 4px rgba(0,0,0,0.8);
}
<canvas id="canvas" width="300" height="300" title="You are over the clipped canvas"></canvas>

CreateJS Radial gradient with matrix

I'm converting a Flash application to HTML5 Canvas. Most of the development is finished but for handling the colors there is a code like this in the flash application:
matrix = new Matrix ();
matrix.createGradientBox (600, ColorHeight * 1200, 0, 80, ColorHeight * -600);
Animation_gradient_mc.clear ();
Animation_gradient_mc.beginGradientFill (fillType, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio);
The declaration for a radial gradient in CreateJS is the following:
beginRadialGradientFill(colors, ratios, x0, y0, r0, x1, y1, r1 )
Does anyone know a method to apply a Matrix to a gradient fill?
Any help would be appreciated.
Thanks in advance
Edit
Here are some examples of the gradient I'm trying to reproduce:
As you can see it starts off as a standard radial gradient.
However, it can also appear stretched, I think this is where the matrix helps.
I've attempted to create the same effect by creating a createjs.Graphics.Fill with a matrix but it doesn't seem to be doing anything:
var matrix = new VacpMatrix();
matrix.createGradientBox(
600,
discharge_gradient.color_height * 1200,
0,
80,
discharge_gradient.color_height * -600
);
// test_graphics.append(new createjs.Graphics.Fill('#0000ff', matrix));
console.log('matrix', matrix);
test_graphics.append(new createjs.Graphics.Fill('#ff0000', matrix).radialGradient(
discharge_gradient.colors,
discharge_gradient.ratios,
discharge_gradient.x0,
discharge_gradient.y0,
discharge_gradient.r0,
discharge_gradient.x1,
discharge_gradient.y1,
discharge_gradient.r1
));
var discharge_shape = new createjs.Shape(test_graphics);
I extended the Matrix2d class to add a createGradientBox method using code from the openfl project:
p.createGradientBox = function (width, height, rotation, tx, ty) {
if (_.isUndefined(rotation) || _.isNull(rotation)) {
rotation = 0;
}
if (_.isUndefined(tx) || _.isNull(tx)) {
tx = 0;
}
if (_.isUndefined(ty) || _.isNull(ty)) {
ty = 0;
}
var a = width / 1638.4,
d = height / 1638.4;
// Rotation is clockwise
if (rotation != 0) {
var cos = math.cos(rotation),
sin = math.sin(rotation);
this.b = sin * d;
this.c = -sin * a;
this.a = a * cos;
this.d = d * cos;
} else {
this.b = 0;
this.c = 0;
}
this.tx = tx + width / 2;
this.ty = ty + height / 2;
}
I hope the extra information is useful.
I don't know createJS enough, nor Flash Matrix object, but to make this kind of ovalGradient with the native Canvas2d API, you will need to transform the context's matrix.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var horizontalScale = .3;
var verticalScale = 1;
var gradient = ctx.createRadialGradient(100/horizontalScale, 100/verticalScale, 100, 100/horizontalScale,100/verticalScale,0);
gradient.addColorStop(0,"green");
gradient.addColorStop(1,"red");
// shrink the context's matrix
ctx.scale(horizontalScale, verticalScale)
// draw your gradient
ctx.fillStyle = gradient;
// stretch the rectangle which contains the gradient accordingly
ctx.fillRect(0,0, 200/horizontalScale, 200/verticalScale);
// reset the context's matrix
ctx.setTransform(1,0,0,1,0,0);
canvas{ background-color: ivory;}
<canvas id="canvas" width="200" height="200"></canvas>
So if you are planning to write some kind of a function to reproduce it, have a look at ctx.scale(), ctx.transform() and ctx.setTransform().
EDIT
As you noticed, this will also shrink your drawn shapes, also, you will have to calculate how much you should "unshrink" those at the drawing, just like I did with the fillRect. (agreed, this one was an easy one)
Here is a function that could help you with more complicated shapes. I didn't really tested it (only with the given example), so it may fail somehow, but it can also give you an idea on how to deal with it :
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
function shrinkedRadial(ctx, shapeArray, xScale, yScale, gradientOpts) {
// scaling by 0 is like not drawing
if (!xScale || !yScale) return;
var gO = gradientOpts;
// apply our scale on the gradient options we passed
var gradient = ctx.createRadialGradient(gO.x0 / xScale, gO.y0 / yScale, gO.r0, gO.x1 / xScale, gO.y1 / yScale, gO.r1);
gradient.addColorStop(gO.c1_pos, gO.c1_fill);
gradient.addColorStop(gO.c2_pos, gO.c2_fill);
// shrink the context's matrix
ctx.scale(xScale, yScale);
ctx.fillStyle = gradient;
// execute the drawing operations' string
shapeArray.forEach(function(str) {
var val = str.split(' ');
var op = shapesRef[val[0]];
if (val[1]) {
var pos = val[1].split(',').map(function(v, i) {
// if even, it should be an y axis, otherwise an x one
return i % 2 ? v / yScale : v / xScale;
});
ctx[op].apply(ctx, pos);
} else {
// no parameters
ctx[op]();
}
});
// apply our gradient
ctx.fill();
// reset the transform matrix
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
// just for shortening our shape drawing operations
// notice how arc operations are omitted, it could be implemented but...
var shapesRef = {
b: 'beginPath',
fR: 'fillRect',
m: 'moveTo',
l: 'lineTo',
bC: 'bezierCurveTo',
qC: 'quadraticCurveTo',
r: 'rect',
c: 'closePath'
};
var gradientOpts = {
x0: 232,
y0: 55,
r0: 70,
x1: 232,
y1: 55,
r1: 0,
c1_fill: 'red',
c1_pos: 0,
c2_fill: 'green',
c2_pos: 1
}
var shapes = ['b', 'm 228,133', 'bC 209,121,154,76,183,43', 'bC 199,28,225,34,233,59', 'bC 239,34,270,29,280,39', 'bC 317,76,248,124,230,133']
// our shape is drawn at 150px from the right so we do move the context accordingly, but you won't have to.
ctx.translate(-150, 0);
shrinkedRadial(ctx, shapes, .3, 1, gradientOpts);
ctx.font = '15px sans-serif';
ctx.fillStyle = 'black';
ctx.fillText('shrinked radialGradient', 3, 20);
// how it looks like without scaling :
ctx.translate(50, 0)
var gO = gradientOpts;
var gradient = ctx.createRadialGradient(gO.x0, gO.y0, gO.r0, gO.x1, gO.y1, gO.r1);
gradient.addColorStop(gO.c1_pos, gO.c1_fill);
gradient.addColorStop(gO.c2_pos, gO.c2_fill);
ctx.fillStyle = gradient;
shapes.forEach(function(str) {
var val = str.split(' ');
var op = shapesRef[val[0]];
if (val[1]) {
var pos = val[1].split(',');
ctx[op].apply(ctx, pos);
} else {
ctx[op]();
}
});
ctx.fill();
ctx.font = '15px sans-serif';
ctx.fillStyle = 'black';
ctx.fillText('normal radialGradient', 160, 20);
<canvas id="canvas" width="400" height="150"></canvas>
A standard matrix would adjust inputs:
Width, angle Horizontal, angle Vertical, Height, pos X, pos Y in that order,
Here you are using gradientBox which is not the usual type of AS3 matrix. Expected input:Width, Height, Rotation, pos X, pos Y
I don't use createJS so I'm gunna guess this (you build on it)...
Your usual beginRadialGradientFill(colors, ratios, x0, y0, r0, x1, y1, r1 )
becomes like below (as though gradientBox matrix is involved):
beginRadialGradientFill(colors, ratios, posX, posY, Rotation, Width, Height, Rotation )

How to show part of element from other side of canvas

How to show part of element outside of canvas from opposite side canvas. Illustration:
You need to draw twice when the shape is outside canvas' boundaries. Draw the main part first, then the same part offset by width so it gives the illusion of showing on the other side.
Manually Draw twice
This draws a shape going from right to left, when the shape is outside the left edge it will be redrawn at the right edge representing the part that is non-visible on the left side. For the opposite way (left to right) the principle is just the same, just use x with canvas' width instead of 0.
var ctx = document.querySelector("canvas").getContext("2d"),
x = 100, // start position
w = 200; // shape width
ctx.fillStyle = "#777";
(function loop() {
ctx.clearRect(0, 0, 300, 150); // clear canvas
ctx.fillRect(x, 0, w, 150); // draw main part/image/shape
if (x < 0) { // should rotate? draw secondary
ctx.fillRect(ctx.canvas.width + x, 0, w, 150); // use canvas width + x (x<0)
}
x -= 7; // animate
if (x <= -w) x = ctx.canvas.width + x; // at some point reset x
requestAnimationFrame(loop)
})();
<canvas></canvas>
Translated Pattern
To simplify this a CanvasPattern can be used. The later version of canvas allows local transforms on the pattern itself, but since this is not currently widely spread I'll show an example using normal transforms and compensated x position:
var ctx = document.querySelector("canvas").getContext("2d"),
pattern,
x = 100, // start position
w = 200; // shape width
// create pattern
ctx.fillStyle = "#777";
ctx.fillRect(x, 0, w, 150); // draw main part/image/shape
pattern = ctx.createPattern(ctx.canvas, "repeat"); // use current canvas as pattern
ctx.fillStyle = pattern; // set pattern as fillStyle
(function loop() {
ctx.setTransform(1,0,0,1,0,0); // reset transforms
ctx.clearRect(0, 0, 300, 150); // clear canvas
ctx.setTransform(1,0,0,1,x,0); // translate absolute x
ctx.fillRect(-x, 0, 300, 150); // fill using pattern, compensate transform
x -= 7; // animate
requestAnimationFrame(loop)
})();
<canvas></canvas>

How to dividing a semicircular region into colored segments using HTML Canvas

trying to divide a semicircular region into colored segments in HTML Canvas.
Here's what I tried,
ctx.save();
ctx.translate(c.width / 2, (c.height / 2)-1);
ctx.strokeStyle = "red"
ctx.lineWidth = 3;
ctx.lineCap = "round";
var x=400; // number of times lineTo strokes. Greater the value the better is the smoothness
var factor=1; //with =1, the entire semicirular region is filled.
for (var i = 0; i < x; i++) {
//ctx.rotate(Math.PI);
ctx.beginPath();
ctx.strokeStyle = "rgba(255,0,0,1)";
//ctx.rotate(-Math.PI/2);
ctx.rotate((-Math.PI * factor) / x);
//1st color segment, factor=1 helps to paint 100% of semicircular region
ctx.moveTo(122, 0);
ctx.lineTo(70, 0);
ctx.stroke();
//ctx.rotate(Math.PI); //2nd color segment
Alternate way, might be to use concentric arc() segments. I'm trying that now. But any one who can throw some light would be a great help.
the sample at http://www.phpied.com/wp-content/uploads/2008/02/canvas-pie.html was the one I was looking for.
Uses concentric arc() as I anticipated.