How can I fill a triangle with gradients starting at its vertices given a color for each vertex?
I'm trying to reproduce something like this:
I'm making use of the built in fill function from the HTML5 canvas Context2D. I'm trying to avoid having to deal with pixel-by-pixel interpolations based on their distance to the vertices. I fear it wont be as performatic as the built-in fill function (?). Also I can't deal with WebGL right now.
I've done a trick using radial gradients, but, there are a few problems with my naive approach:
The colors don't seem to blend well
The last applied gradient overwrites the others
The value used in the radius variable is arbitrary
OBS: I don't know if it's relevant but, I'm building a triangle strip (indexed geometry actually).
var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
var v1 = { x: 100, y: 0 };
var v2 = { x: 0, y: 180 };
var v3 = { x: 200, y: 180 };
var radius = 175;
var grd1 = ctx.createRadialGradient(v1.x, v1.y, 0, v1.x, v1.y, radius);
grd1.addColorStop(0, "#FF0000FF");
grd1.addColorStop(1, "#FF000000");
var grd2 = ctx.createRadialGradient(v2.x, v2.y, 0, v2.x, v2.y, radius);
grd2.addColorStop(0, "#00FF00FF");
grd2.addColorStop(1, "#00FF0000");
var grd3 = ctx.createRadialGradient(v3.x, v3.y, 0, v3.x, v3.y, radius);
grd3.addColorStop(0, "#0000FFFF");
grd3.addColorStop(1, "#0000FF00");
ctx.beginPath();
ctx.moveTo(v1.x, v1.y);
ctx.lineTo(v2.x, v2.y);
ctx.lineTo(v3.x, v3.y);
ctx.closePath();
ctx.fillStyle = "#FFFFFFFF"; // fill with white and apply the gradients on top of it
ctx.fill();
ctx.fillStyle = grd1;
ctx.fill();
ctx.fillStyle = grd2;
ctx.fill();
ctx.fillStyle = grd3;
ctx.fill();
<canvas width="200" height="180"></canvas>
The colors don't seem to blend well
For this you can use the globalCompositeOperation property of your 2D context to one of its blend modes, even though in your case the compositing mode "lighter" with a black background seems to produce the closest result to your model.
The last applied gradient overwrites the others
Thanks to the previous bullet point, it's not the case anymore.
The value used in the radius variable is arbitrary
Doesn't look like so to me, it does correspond to the distance between every points of your equilateral triangle and its center, which makes perfect sense.
var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
// reordered to make the same as OP's image
var v1 = { x: 0, y: 180 };
var v2 = { x: 200, y: 180 };
var v3 = { x: 100, y: 0 };
var radius = 180;
var grd1 = ctx.createRadialGradient(v1.x, v1.y, 0, v1.x, v1.y, radius);
grd1.addColorStop(0, "#FF0000FF");
grd1.addColorStop(1, "#FF000000");
var grd2 = ctx.createRadialGradient(v2.x, v2.y, 0, v2.x, v2.y, radius);
grd2.addColorStop(0, "#00FF00FF");
grd2.addColorStop(1, "#00FF0000");
var grd3 = ctx.createRadialGradient(v3.x, v3.y, 0, v3.x, v3.y, radius);
grd3.addColorStop(0, "#0000FFFF");
grd3.addColorStop(1, "#0000FF00");
ctx.beginPath();
ctx.moveTo(v1.x, v1.y);
ctx.lineTo(v2.x, v2.y);
ctx.lineTo(v3.x, v3.y);
ctx.closePath();
// fill with black
ctx.fill();
// set blend mode
ctx.globalCompositeOperation = "lighter";
ctx.fillStyle = grd1;
ctx.fill();
ctx.fillStyle = grd2;
ctx.fill();
ctx.fillStyle = grd3;
ctx.fill();
// if you need to draw something else, don't forget to reset the gCO
ctx.globalCompositeOperation = "source-over";
<canvas width="200" height="180"></canvas>
Related
I am trying to using context.clip() to clip a draw arc from other one and fill the clipped result.
But when i clip section & fill it, it gives pixelated fill .
var ctx = document.getElementById("canvas").getContext("2d");
var x = 150 ;
var y = 150 ;
var r = 100 ;
ctx.save() ;
ctx.translate(x,y) ;
ctx.beginPath() ;
ctx.arc(0,0,r,0,2*Math.PI);
ctx.closePath() ;
ctx.fillStyle = "cyan" ;
ctx.fill() ;
ctx.lineWidth = 10;
ctx.stroke();
ctx.restore() ;
ctx.save() ;
ctx.clip() ;
ctx.translate(x,y);
ctx.beginPath();
ctx.moveTo(r,-r-10);
ctx.arc(0,-r-10,r,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle = "#f2f2f2";
ctx.fill();
ctx.lineWidth = 1;
ctx.stroke();
ctx.restore();
https://jsfiddle.net/x0d0n40z/1/
An alternative approach which eliminates the need for clip()/save()/restore() is to use a few steps of compositing.
Clipping mask is anti-aliased in some browsers while in other not. To obtain consistency (and in some cases also performance since save-clip-restore are relative expensive operations) using composition is preferred if possible.
In this case:
Fill main arc in target color
Define a clipping arc
Change composite mode to destination-out and fill (will cut main)
Change composite mode to source-atop and stroke (will outline cut)
Change composite mode to source-over and stroke outline of main circle
Example
Update: Simplified steps (with the last step merged into the process, ref. comments). I also chose to demonstrate use of the Path2D since we can reuse the object without interfering with the path on the ordinary context -
var ctx = c.getContext("2d"),
p = new Path2D(), // this will store main shape for reuse
x = 75, y = 75, radius = 70;
// main arc
p.arc(x, y, radius, 0, 6.28); // store to path object
ctx.fillStyle = "cyan";
ctx.fill(p); // fill path object
// clip top arc
ctx.globalCompositeOperation = "source-atop";
ctx.arc(x, y - radius, radius, 0, 6.28);
ctx.fillStyle = "#09f";
ctx.fill();
ctx.lineWidth = 5;
ctx.stroke();
// stroke main arc
ctx.globalCompositeOperation = "source-over";
ctx.stroke(p); // stroke path object
body {background:#e9e9e9}
<canvas id=c></canvas>
Old version:
var ctx = c.getContext("2d"),
x = 75, y = 75, radius = 70;
// main arc
ctx.arc(x, y, radius, 0, 6.28);
ctx.fillStyle = "cyan";
ctx.fill();
// clipping arc
ctx.beginPath();
ctx.arc(x, y - radius, radius, 0, 6.28);
// cut step
ctx.globalCompositeOperation = "destination-out";
ctx.fill();
// stroke gap step
ctx.globalCompositeOperation = "source-atop";
ctx.lineWidth = 10;
ctx.stroke();
// stroke whole outline
ctx.globalCompositeOperation = "source-over";
ctx.beginPath();
ctx.arc(x, y, radius, 0, 6.28);
ctx.lineWidth = 5;
ctx.stroke();
// if you want to color the clip then use this:
ctx.globalCompositeOperation = "destination-atop";
ctx.fillStyle = "#09f";
ctx.fill();
body {background:#e9e9e9}
<canvas id=c></canvas>
The problem is that the clip boundary is not being anti alised.
To solve you can render the shape without using the clip. The ctx.arc method lets you set the start and end angles so you can get the inset by filling two arcs.
You will need to get the angles where the clip circle and the inset circle intercept.
For this case it is very simple. First get the distance between the circles, and the angle from one to the other. This works only for two circles of same radius.
var c = {x:?,y:?}; // circle one location
var c1 = {x:?,y:?}; // circle two location
var radius = ?; // radius of both
var angle = Math.atan2(c1.y - c.y, c1.x - c.x); // get the angle from one to the next
var dist = Math.hypot(c1.x - c.x, c1.y - c.y); // get the distance. NOTE IE does not have hypot so do it the normal way with Math.sqrt....
Now you have the angle and distance the intercepts are a simple relationship between the distance and the radius
var iAngle = Math.acos(dist / 2 / radius); // the angle from the line between the circles
// to the intercepts
Now you have that angle you can draw the two arcs
ctx.beginPath();
ctx.arc(c.x,c.y,radius,angle - iAngle , angle + iAngle); // first arc
ctx.arc(c1.x,c1.y, radius, angle + Math.PI - iAngle, angle + Math.PI + iAngle); // second arc
ctx.fill();
ctx.stroke();
There is not much you can do to prevent the jaggies from effecting the clip area. Another way to achieve clipping is to use ctx.globalCompositeOperation to render a mask. You can mask in and out, and many more options. This will be a better solution when the clipping area becomes more complex.
I finally figured the right way to correct the bug .
Heres the clean result of what i wanted https://jsfiddle.net/x0d0n40z/6/
Code :
var ctx = document.getElementById("canvas").getContext("2d");
var r = 50
x = ctx.canvas.width/2;
y = ctx.canvas.height/2;
var offset = 60;
ctx.save();
ctx.setTransform(1,0,0,1.5,x,y);
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI);
ctx.stroke();
ctx.clip();
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI,false);
ctx.fillStyle = "cyan";
ctx.fill();
ctx.setTransform(1, 0, 0, 1, x, y);
ctx.beginPath();
ctx.arc(0,-offset,r,0,2*Math.PI,false);
ctx.fillStyle = "#f2f2f2";
ctx.fill();
ctx.lineWidth = 1 ;
ctx.stroke();
ctx.setTransform(1,0,0,1.5,x,y);
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI,false);
ctx.lineWidth = 3 ;
ctx.stroke();
ctx.restore();
Source from were i learned to use clip : http://www.html5canvastutorials.com/advanced/html5-canvas-clipping-region-tutorial/
Say I have created a polygon-shaped image by creating a shape in HTML5 canvas and then filling it with an image, e.g. as below:
Now I want to round the corners on this hexagon.
There is a lineJoin = "round" property available but this doesn't seem to work (I believe because the shape is filled and there is no outer line to round).
Does anyone have any idea how to do this with HTML5 canvas or any other means?
Here is the code used to create the image:
var ctx = document.getElementById('myCanvas').getContext('2d');
var a = ctx.canvas.width, r = a / 5;
var side = Math.sqrt((4/3) * r * r);
// Draw your image onto the canvas (here I'll just fill the
// surface with red
var img = new Image();
img.src = "https://upload.wikimedia.org/wikipedia/commons/0/03/Mountain_Bluebird.jpg";
img.onload = function () {
var pattern = ctx.createPattern(img, "no-repeat");
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, a, a);
// Switch the blending mode
ctx.globalCompositeOperation = 'destination-in';
// Draw the hexagon shape to mask the image
ctx.beginPath();
ctx.moveTo(0, side/2);
ctx.lineTo(0, 3*side/2);
ctx.lineTo(r, 2*side);
ctx.lineTo(2*r, 3*side/2);
ctx.lineTo(2*r, side/2);
ctx.lineTo(r, 0);
ctx.closePath();
ctx.fill();
};
<canvas width="1000" height="1000" id="myCanvas"></canvas>
Just change the order you draw in and then change globalCompositeOperation to 'source-in'.
I made some adjustments because some of the corners in your code were getting clipped off but didn't adjust the image position (I hope that's easy enough to do)
Preview
You need to adjust the image position by the way like I said
Snippet
var ctx = document.getElementById('myCanvas').getContext('2d');
var a = ctx.canvas.width, r = a / 5;
var side = Math.sqrt((4/3) * r * r) - 20;
// Draw your image onto the canvas (here I'll just fill the
// surface with red
var img = new Image();
img.src = "https://upload.wikimedia.org/wikipedia/commons/0/03/Mountain_Bluebird.jpg";
img.onload = function () {
ctx.lineJoin = "round";
ctx.lineWidth = 50;
// Draw the hexagon shape to mask the image
ctx.beginPath();
ctx.moveTo(50, 50 + side/2);
ctx.lineTo(50, 50 + 3*side/2);
ctx.lineTo(50 + r, 50 + 2*side);
ctx.lineTo(50 + 2*r, 50 + 3*side/2);
ctx.lineTo(50 + 2*r, 50 + side/2);
ctx.lineTo(50 + r, 50);
ctx.closePath();
ctx.stroke();
ctx.fill();
// Switch the blending mode
ctx.globalCompositeOperation = 'source-in';
var pattern = ctx.createPattern(img, "no-repeat");
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, a, a);
};
<canvas width="1000" height="1000" id="myCanvas"></canvas>
There is no automatic way of doing this. The canvas API is pretty low-level, so it doesn't know that you've drawn a shape and want to detect where all the edges are. What you can do, but it would be a bit of a hassle to do is use bezierCurveTo. This method takes size arguments and can create curves. Combine that with your lines, and you can create rounded corners.
bezierCurveTo
You could also add circles at all the corners, with the arc method.
I would like to create a circle with a special shadow effect.
Like this one:
.
It should look like a cone in wood or metal.
I tried to do something with the radial gradiant in canvas but i can't creat that special Effekt.
I donĀ“t know how to create the this shadow effect.
Can somebody give me a tip or help me?
This is what I've tried: JSFiddle
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 100,
y = 75,
innerRadius = 1,
outerRadius = 70,
radius = 60;
ctx.arc(x, y, radius, 0, 2 * Math.PI);
var gradient = ctx.createRadialGradient(x, y, innerRadius, x, y, outerRadius);
gradient.addColorStop(0, '#FF9900');
gradient.addColorStop(1, '#FFFFFF');
ctx.fillStyle = gradient;
ctx.fill();
Greetings from Germany
Matzuman
There is unfortunately no gradient type in canvas which allow you to specify a radiant gradient. You have to provide a mechanism to do so manually.
You could use a shadow approach drawing the object off-screen while offsetting the shadow so it overlapped the cone base. One for light and one for dark side.
You can achieve a better effect though by drawing a "light/dark stripe" rotated around the center at varying opacity levels depending on angle.
Example "rendering" the cone
This example allow you to adjust parameters like how visible the reflections should be, what colors, size of cone etc. Experiment with the values to find what you're after.
To offset the "light source", just rotate one time initially with the angle you need before rendering the overlapping stripes.
var ctx = document.querySelector("canvas").getContext("2d"),
cx = 75, cy = 75, radius = 70, // for arc/cone
maxOpacity = 1, // max opacity (will accumulate)
angleStep = 0.01, // "resolution"
angle = 0, t; // current angle and opacity
// draw base of cone
ctx.fillStyle = "rgb(139, 108, 33)";
ctx.arc(cx, cy, radius, 0, 2*Math.PI);
ctx.fill();
// now rotate around center drawing a white stripe at varying opacity
// depending on angle
ctx.fillStyle = "rgb(181, 159, 109)";
ctx.translate(cx, cy); // pivot for rotation = center of cone
// half of the cone is done with white overlay
for(angle = 0; angle < Math.PI; angle += angleStep) {
// calculate t [0,1] based on angle. Multiply with max opacity
t = (angle < Math.PI * 0.5 ? angle : Math.PI - angle) / Math.PI * maxOpacity;
ctx.rotate(angleStep); // increase angle by step
ctx.globalAlpha = t; // set opacity to t
drawStripe(); // draw a small segment / "stripe"
}
// the other half of the cone is done with dark overlay
ctx.fillStyle = "rgb(95, 54, 5)";
for(angle = 0; angle < Math.PI; angle += angleStep) {
t = (angle < Math.PI * 0.5 ? angle : Math.PI - angle) / Math.PI * maxOpacity;;
ctx.rotate(angleStep);
ctx.globalAlpha = t;
drawStripe();
}
function drawStripe() {
ctx.beginPath();
ctx.lineTo(0, 0);
ctx.arc(0, 0, radius, 0, angleStep*5);
ctx.fill();
}
// top off by drawing a smaller circle on top
ctx.setTransform(1,0,0,1,0,0); // reset transforms
ctx.globalAlpha = 1; // reset alpha
ctx.fillStyle = "rgb(130, 97, 32)"; // draw in a topping
ctx.beginPath();
ctx.arc(cx, cy, radius * 0.25, 0, 2*Math.PI);
ctx.fill();
<canvas></canvas>
Example approximating a cone shape with shadows
var ctx = document.querySelector("canvas").getContext("2d"),
cx = 75, cy = 75, radius = 70, offset = radius * 2;
// draw base of cone
ctx.fillStyle = "rgb(139, 108, 33)";
ctx.arc(cx, cy, radius, 0, 2*Math.PI);
ctx.fill();
// offset next shape, couter-offset its shadow
ctx.translate(cx, offset*2); // make sure shape is drawn outside
ctx.scale(0.75, 1); // make shadow more narrow
ctx.globalCompositeOperation = "source-atop"; // comp. on top of existing pixels
ctx.shadowOffsetY = -offset * 1.1; // counter-offset shadow
ctx.shadowBlur = 25; // some blur
ctx.shadowColor = "rgba(181, 159, 109, 1)"; // highlight color
ctx.beginPath(); // draw new shape
ctx.arc(0, 0, radius * 0.6, 0, 2*Math.PI); // reduce radius ~50%
ctx.fill();
ctx.shadowOffsetY = -offset * 1.8; // counter-offset shadow
ctx.shadowColor = "rgba(95, 54, 5, 0.7)"; // shadow
ctx.beginPath(); // draw new shape
ctx.arc(0, 0, radius * 0.6, 0, 2*Math.PI); // reduce radius ~50%
ctx.fill();
// top off by drawing a smaller circle on top
ctx.setTransform(1,0,0,1,0,0); // reset transforms
ctx.globalCompositeOperation = "source-over"; // reset comp. mode
ctx.fillStyle = "rgb(130, 97, 32)"; // draw in a topping
ctx.beginPath();
ctx.arc(cx, cy, radius * 0.25, 0, 2*Math.PI);
ctx.fill();
<canvas></canvas>
Try using
ctx.shadowBlur = 40;
ctx.shadowColor = "#FF9900";
before drawing the circle.
shadowBlur sets the size of the shadow, you can set it to 0 if you want to disable it.
shadowColor is pretty self explanatory.
I want to crop image over another image like a pie-chart to create a loading animation. I was thinking of using raphaeljs, but couldn't find any information about image cropping in pie-chart style.
Here are the sample images:
Start state:
End state:
What it should look like:
Just draw a semi-transparent filled arc on top of the image (adjust alpha value to your pleasing):
var ctx = document.querySelector("canvas").getContext("2d"),
img = new Image;
img.onload = draw;
img.src = "http://i.imgur.com/hQ5Pljv.png";
function draw(){
var cx = 157, cy = 159, r = 150,
pst = 0,
ang = Math.PI * 2 * (pst/100),
dlt = 2;
// animate the following part
(function loop() {
ctx.drawImage(img, 0, 0);
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, r, 0, ang);
ctx.fillStyle = "rgba(0,0,0,0.33)"; // adjust alpha here
ctx.fill();
pst += dlt;
if (pst <= 0 || pst >= 100) dlt = -dlt;
ang = Math.PI * 2 * (pst/100);
requestAnimationFrame(loop)
})()
}
<canvas width=320 height=320></canvas>
Method two - compositing
Use two steps to clip the same arc above to use images instead:
Draw arc, this will be the composite data
Change comp. mode to source-atop - next drawing replaces the drawn arc
Draw secondary image in
Change comp. mode to destination-atop - next drawing will fill all non-pixels
Draw main image in
Demo:
var ctx = document.querySelector("canvas").getContext("2d"),
img1 = new Image, img2 = new Image, cnt=2;
img1.onload = img2.onload = loader;
img1.src = "http://i.imgur.com/hQ5Pljv.png";
img2.src = "http://i.imgur.com/k70j3qp.jpg";
function loader(){if (!--cnt) draw()};
function draw(){
var cx = 157, cy = 159, r = 150,
pst = 0, ang = Math.PI * 2 * (pst/100), dlt = 2;
// animate the following part
(function loop() {
ctx.clearRect(0, 0, 320, 320); // clear canvas, or set last comp mode to "copy"
// first arc
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, r, 0, ang);
ctx.fill(); // this will be comp. basis for the next steps
// comp mode secondary image
ctx.globalCompositeOperation = "source-atop"; // replaces filled arc
ctx.drawImage(img2, 0, 0);
// comp mode main image
ctx.globalCompositeOperation = "destination-atop"; // fills all non-pixels
ctx.drawImage(img1, 0, 0);
pst += dlt; if (pst <= 0 || pst >= 100) dlt = -dlt; ang = Math.PI * 2 * (pst/100);
ctx.globalCompositeOperation = "source-over"; // reset comp. mode
requestAnimationFrame(loop)
})()
}
<canvas width=320 height=320></canvas>
You'll want an algorithm along the lines of:
Draw image A onto canvas 1
Clear canvas 2
Draw a partial circle on canvas 2, for the current state of the spinner, filled with white
Blit image B onto canvas 2, using the multiplicative blending mode
Blit canvas 2 onto canvas 1, using standard (replace) blending
Canvas 2 should contain the second image, masked by the section you want to use. Overlaying that onto canvas 1, provided you handle transparency properly, should give the effect you want.
You can also use two SVG circles with image backgrounds and do this trivially, assuming your target browsers support SVG.
PEN: https://codepen.io/jaredstanley/pen/gvmNye
var canvas = document.getElementById('c');
var ctx = canvas.getContext("2d");
var centerw = canvas.width/2;
var centerh = canvas.height/2;
var sq_w = 80;
//
ctx.beginPath();
//draw rectangle
ctx.rect(this.centerw-(sq_w/2), 0,sq_w, canvas.height);
//draw circle
ctx.arc(this.centerw, this.centerh, 185, 0, Math.PI * 2, true);
//fill
ctx.fill();
The shapes both draw but the intersection of the shapes is blank.
Looking to have one single, filled shape, but get the following result:[
REQUIREMENTS:
Cannot use CanvasRenderingContext2D.globalCompositeOperation as I'm using that for something else; this needs to be used as a single shape so i can use the shape to ...clip().
Note: when using two rect() calls it works, and when using two arc() calls it works, but mixing them seems to cause an issue.
Seems like it should be easy but I'm stumped, missing something basic I think. Thanks!
Path-direction matters
Simply remove (or set to false) the counter-clock wise flag on the arc() method as this will otherwise define the path the "opposite" direction affecting the default non-zero winding algorithm used for filling:
//ctx.arc(this.centerw, this.centerh, 185, 0, Math.PI * 2, true); ->
ctx.arc(this.centerw, this.centerh, 185, 0, Math.PI * 2);
A More Close Look at "Non-Zero Winding"
According to the non-zero winding rule we would add up winding counted from a point from where a line is "sent out". For each line intersection of the point's line we check the crossing line's direction and give it +1 for one direction, -1 if the opposite direction, and add those together.
To illustrate:
For the illustration on the left we can see that the sum of the directions of the two first line intersections (if point is placed left and center on y) will be 0 ("zero") so no fill for the center section. This would also happen if a point sent a line from center top and down through the shape.
However, in the illustration on the right the sum is non-zero when we come to the inner section so it too becomes filled.
Example: arc() uses clockwise direction instead
var canvas = document.getElementById('c');
var ctx = canvas.getContext("2d");
var centerw = canvas.width/2;
var centerh = canvas.height/2;
var sq_w = 120;
//
ctx.beginPath();
//draw rectangle
ctx.rect(centerw-(sq_w/2), 0,sq_w, canvas.height);
//draw circle
ctx.moveTo(centerw + 185, centerh); // create new sub-path (is unrelated, see below)
ctx.arc(centerw, centerh, 185, 0, Math.PI * 2); // <- don't use the CCW flag
//fill
ctx.fill();
<canvas id="c" width="500" height="500"></canvas>
Unrelated but something to have in mind: you would also want to create a new sub-path for the arc to avoid risking a line from a corner of the rect going to the start-angle point on the arc. Simply add this line before adding the arc:
ctx.moveTo(centerw + 185, centerh);
ctx.arc(centerw, centerh, 185, 0, Math.PI * 2);
ctx.beginPath();
//draw rectangle
ctx.rect(this.centerw - (sq_w / 2), 0, sq_w, canvas.height);
ctx.fill();
//draw circle
ctx.beginPath();
ctx.arc(this.centerw, this.centerh, 185, 0, Math.PI * 2, true);
//fill
ctx.fill();
The result you see happens because the standard operation on a surface contained by crossed paths, is to ignore.
var canvas = document.getElementById('c');
var ctx = canvas.getContext("2d");
var centerw = canvas.width/2;
var centerh = canvas.height/2;
var sq_w = 80;
//draw rectangle
ctx.fillRect(this.centerw-(sq_w/2), 0,sq_w, canvas.height);
//draw circle
ctx.arc(this.centerw, this.centerh, 185, 0, Math.PI * 2, true);
//fill
ctx.fill();
<canvas id='c' height=500 width=500/>
The shapes need to be filled between the rounds. Or, in the code snippet, I changed ctx.rect to ctx.fillRect.
Another approach would be to begin a new path before the arc.