HTML5 Canvas Clip using Arc is clipping wrong - javascript

So I have tried clipping an image using ctx.clip and ctx.arc like this:
ctx.beginPath();
ctx.arc(250, 250, 250, -Math.PI / 4, Math.PI / 4);
ctx.clip();
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
As you see I have set the start angle to -45 deg and the end angle to 45 deg, but what I get is a cut of circle/half moon, not a pac-man figure, as you would see if you filled the arc using ctx.fill.
FIDDLE
Why is this and how do I fix it?
Thanks in advance.

Not knowing anything about HTML5 or JavaScript, I think you are just not closing the path properly. Add ctx.lineTo(250,250); after the arc: http://jsfiddle.net/7em21gvk/

Okay, so I found the problem, simply add ctx.moveTo(x, y) where x and y is the center of the arc, like this:
ctx.beginPath();
ctx.moveTo(250, 250);
ctx.arc(250, 250, 250, -Math.PI / 4, Math.PI / 4);
ctx.clip();
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
FIDDLE

Related

Overlapping clipping issue

I have an issue where I am grabbing a users displayAvatar() and then I use an arc to make the image round. This works fine, however, I need to place a circle on top of that image, but it is getting cut in half because of the previous clip()
Without clip() : https://i.gyazo.com/b474c656f33a1f004f5e3becffcef527.png
With clip() : https://i.gyazo.com/da13377cd3f6dc7516c2b8fd1f0f8ac9.png
I know that in the 'With clip()' image, it appears as if the arc border is showing outside of the clip, but that is hardcoded into the image, I put it as a guide with an image editor.
I tried moving around the code, I removed the line ctx.clip() and saw that my circle displays fine on top of the image.
// circle around avatar
ctx.beginPath();
ctx.arc(122.5, 141.8, 81, 0, Math.PI * 2, true);
ctx.closePath();
ctx.clip();
const avatar = await Canvas.loadImage(
message.author.displayAvatarURL({ format: 'png' })
);
ctx.strokeStyle = '#ffffff';
ctx.strokeRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(avatar, 41.5, 60.5, 162, 162);
// presence circle
ctx.beginPath();
ctx.arc(184.5, 193.5, 19, 0, Math.PI * 2, true);
ctx.strokeStyle = '#000000';
ctx.lineWidth = 8;
ctx.stroke();
ctx.fillStyle = userStatusColor;
ctx.fill();
Take a look at the canvas clip() definition:
https://www.w3schools.com/tags/canvas_clip.asp
Tip: Once a region is clipped, all future drawing will be limited to the clipped region (no access to other regions on the canvas). You can however save the current canvas region using the save() method before using the clip() method, and restore it (with the restore() method) any time in the future.
Below is an example using the save and restore
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(90, 90, 81, 0, Math.PI * 2, true);
ctx.stroke();
ctx.save();
ctx.clip();
ctx.beginPath();
ctx.arc(150, 50, 19, 0, Math.PI * 2, true);
ctx.fillStyle = '#0000ff';
ctx.lineWidth = 8;
ctx.stroke();
ctx.fill();
ctx.restore();
ctx.beginPath();
ctx.arc(170, 99, 19, 0, Math.PI * 2, true);
ctx.fillStyle = '#ff0000';
ctx.lineWidth = 8;
ctx.stroke();
ctx.fill();
<canvas id="canvas"></canvas>

How can we take advantage of moveTo( ) HTML5 method?

please I am a little confused so I need your help .
My question is how can we take advantage of moveTo() html5 method ?
for example I found this example on stackOverflow
function drawSmile(ctx, x, y, faceRadius, eyeRadius) {
ctx.save(); // save
ctx.fillStyle = '#FF6'; // face style : fill color is yellow
ctx.translate(x, y); // now (x,y) is the (0,0) of the canvas.
ctx.beginPath(); // path for the face
ctx.arc(0, 0, faceRadius, 0, 6.28);
ctx.fill();
ctx.fillStyle = '#000'; // eye style : fill color is black
ctx.beginPath(); // path for the two eyes
ctx.arc(faceRadius / 2, - faceRadius /3, eyeRadius, 0, 6.28);
ctx.moveTo(-faceRadius / 2, - faceRadius / 3); // sub path for second eye
ctx.arc(-faceRadius / 2, - faceRadius / 3, eyeRadius, 0, 6.28);
ctx.fill();
ctx.restore(); // context is just like before entering drawSmile now.
}
drawSmile(c, 200,200, 60, 12);
but when I removed the line number 11 in the code which uses the moveTo method no thing changed!!!!.
The moveTo() HTML5 method lets you to move your (0,0) origin to another point in the space.
Here you have and example. To draw some kind of triangle:
// first part of the path
ctx.moveTo(20,20);
ctx.lineTo(100, 100);
ctx.lineTo(100,0);
// second part of the path
ctx.moveTo(120,20);
ctx.lineTo(200, 100);
ctx.lineTo(200,0);
// indicate stroke color + draw the path
ctx.strokeStyle = "#0000FF";
ctx.stroke();
In this example we simply called moveTo(x, y) after drawing the first part of the path (the shape on the left). Then, we only called stroke() once to draw the whole path.

Problems with using fill() in a canvas - illogical behaviour

I'm trying to learn how to draw/fill different shapes by using canvas and JavaScript, but my shapes doesn't get filled in the way I want them to, at all. The body of my HTML-document is this simple line:
<canvas id="canvas1" width="500" height="500"></canvas>
And my JavaScript-file looks like this:
function draw() {
var canvas1 = document.getElementById('canvas1');
if(canvas1.getContext) {
var ctx = canvas1.getContext('2d');
var gradient = ctx.createLinearGradient(0, 0, 50, 0);
gradient.addColorStop(0, "blue");
gradient.addColorStop(1, "white");
ctx.beginPath();
ctx.moveTo(25,25);
ctx.lineTo(100, 25);
ctx.stroke();
ctx.moveTo(25, 50);
ctx.bezierCurveTo(25, 50, 50, 80, 75, 60)
ctx.fillStyle = "black";
ctx.fill();
ctx.beginPath();
ctx.moveTo(75, 100);
ctx.arc(50, 100, 25, 0, Math.PI*2, true);
ctx.fillStyle = "black";
ctx.fill();
ctx.beginPath();
ctx.fillStyle = gradient;
ctx.arc(75, 150, 25, 0, Math.PI*2, true);
ctx.fill();
}
}
But this is the result:
And I don't get it. I've tried filling my second circle with every other color, and that works just fine. Also if I remove the last "ctx.beginPath();" my first circle gets painted in gradient. But I can't get the same bug to work on my second circle by changing the position of the code or something. And every guide I've found tells me that this should work, as far as I understand it.
Gradients are defined with an absolute position so if you draw your circle outside the area defined by the gradient it will appear transparent instead of filled.
There is no need to close the path as the fill() method will close it implicit for you, but just make sure the coordinates in the gradient covers the area you want to fill.
Instead of calculating for each time you need to fill an arc you could create a generic wrapper function which takes a position and colors to fill (adjust as needed):
A demo here
/**
* Fills a circle with a two-color gradient.
* #param {Number} cx - center X
* #param {Number} cy - center Y
* #param {Number} radius - radius
* #param {String} col1 - start color as CSS color string
* #param {String} col2 - end color as CSS color string
* #param {Boolean} [horiz=false] - Set true for horizontal gradient
*/
function fillCircle(cx, cy, radius, col1, col2, horiz) {
var x = cx - radius,
y = cy - radius,
d = radius * 2,
gradient;
if (horiz) {
gradient = ctx.createLinearGradient(x, 0, x+d, d);
}
else {
gradient = ctx.createLinearGradient(0, y, 0, y+d);
}
gradient.addColorStop(0, col1);
gradient.addColorStop(1, col2);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, 2*Math.PI);
ctx.fill();
}
Then just use it this way:
fillCircle(200, 200, 70, 'yellow', 'red');
The last flag is optional here and makes a horizontal gradient if set to true.
Use ctx.closePath(); After each separate shape/line you want is done.
ctx.beginPath();
ctx.moveTo(25, 50);
ctx.bezierCurveTo(25, 50, 50, 80, 75, 60)
ctx.strokeStyle = "black";
ctx.stroke();
ctx.closePath();
The gradient needs to be set with the coordinates matching where your shape is on the canvas.
You have the gradient starting at 0,0,
var gradient = ctx.createLinearGradient(0, 0, 50, 0);
But your circle is locates at 25,50. Make your gradient coordinates the same as you circle coordinates.
http://jsfiddle.net/bC75t/1/

coloring a line on canvas

I am trying to color the following line, but my canvas either colors all the lines or does not color at all. Any help would be appreciated
canvas.save();
canvas.scale(1, 0.75);
canvas.beginPath();
canvas.arc(100, 95, 8, 0, Math.PI * 2, false);
canvas.stroke();
canvas.strokeStyle= "red";
canvas.closePath();
canvas.restore();
You are using canvas, I assume you mean context.
canvas=getElementById("mycanvas");
context.getContext("2d");
A few points:
1. Start 1 or more draws with context.beginPath();
2. When you tell the context to context.stroke(), it will use the last strokeStyle you set (previous strokeStyles are ignored)
3. always to context.stroke() to physically apply your drawn lines,arcs,etc to the canvas.
// draw a red circle
context.beginPath();
context.arc(100, 95, 8, 0, Math.PI * 2, false);
context.strokeStyle="red";
context.stroke();
//then begin a new path and draw a blue circle
context.beginPath();
context.arc(150, 95, 8, 0, Math.PI * 2, false);
context.strokeStyle="blue";
context.stroke();

html5 canvas - merge two clipping regions - James Bond Gunbarrel

I have just started using canvas to get this kind of effect:
"James Bond Gunbarrel View Start"
http://www.youtube.com/watch?v=sfXazFp68cE
I managed to get almost there:
http://jsbin.com/uyaker/8/edit
Now as you can see I clip my canvas with two circles (At least I try to) ... but the problem is the region that overlaps is not clipped anymore ...
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(canvas.width, 0);
ctx.lineTo(canvas.width, canvas.height);
ctx.lineTo(0, canvas.height);
ctx.closePath();
ctx.moveTo(cx + r, cy);
ctx.arc(cx, cy, r, 0, Math.PI*2, true);
// check if to draw a fixed circle, every 200 pixels for 100 pixels
if(goingright && cx % 200 < 100) {
ctx.moveTo(cx - cx % 200 + r, cy);
ctx.arc(cx - cx % 200, cy, r, 0, Math.PI*2, true);
}
ctx.closePath();
ctx.clip();
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
Maybe this effect is possible without using clipping but give the canvas a css background-image and draw everything but the circles ... but I don't really know how to do that :/.
Maybe this effect is possible without using clipping ... but I don't really know how to do that
Yes it is possible, you need to create a path that will do all the work:
context.beginPath();
context.moveTo(x, y);
// Draw your big shape, in your case is a rectangle (4 point)
context.lineTo(xn, yn);
context.closePath();
// Now the context knows that every path that will be added without .beginPath(), will clip the current path
context.arc(cx, cy, r, 0, Math.PI * 2, true);
context.closePath();
context.fill(); // Fill with color all the area except the arc
Example: http://jsfiddle.net/drhWb/
Saving, restoring and clipping the context are very expensive operations so you should use this approach is the right way you need to go.

Categories