Which of the two overlapping graphs will ctx.fill method fill? - javascript

I have two slightly different pieces of code which produce different results. In the first piece, I draw the inside triangle first, then the outside square. In the second piece, I draw the outside square first, then the inside triangle. However, for the second piece of code, the entire square is filled. Why is this happening?
<body>
<canvas id="c" width="300" height="300"></canvas>
<script>
var c = document.getElementById('c');
var ctx = c.getContext('2d');
ctx.moveTo(75 ,75);
ctx.lineTo(125, 75);
ctx.lineTo(125, 125);
ctx.lineTo(75, 75);
ctx.fill();
ctx.moveTo(50, 50);
ctx.lineTo(150,50);
ctx.lineTo(50,150);
ctx.lineTo(50,50);
ctx.stroke();
</script>
</body>
The result of the above code
<body>
<canvas id="c" width="300" height="300"></canvas>
<script>
var c = document.getElementById('c');
var ctx = c.getContext('2d');
ctx.moveTo(50, 50);
ctx.lineTo(150,50);
ctx.lineTo(50,150);
ctx.lineTo(50,50);
ctx.stroke();
ctx.moveTo(75 ,75);
ctx.lineTo(125, 75);
ctx.lineTo(125, 125);
ctx.lineTo(75, 75);
ctx.fill();
</script>
</body>
The result of the above code

Use ctx.beginPath to clear old path and start a new one.
When you use path construction methods like, ctx.rect, ctx.arc, ctx.moveTo, ctx.lineTo, ctx.quadraticCurveTo, ctx.bezierCurveTo, ctx.ellipse, ctx.arcTo and ctx.closePath you are adding to the current path held in the 2D context memory.
When you call ctx.fill or ctx.stroke you render the current stored path. The path stored is continually added to with the 1st paragraphs path constructor methods. The path stays stored until you call ctx.beginPath which will clear the current path and start a new one.
Note that resizing the canvas will also clear the current path and reset the canvas context to its default state, though it is not recommended as a way to revert to the default state.
So to correctly render the two paths use ctx.beginPath to create a new path for each you want to render.
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
ctx.beginPath(); // begin a new path
// Note that the path is offset by 0.5
// A line is defined by its center. A line 1 pixel wide has half the line
// above and half below. If you render at the pixel boundary from 50,50 to 100,50
// half the line is rendered on the 49th row and half on the 50th row.
// Offsetting by 0.5 ensures the line is rendered on the pixels you want
// producing a better quality line.
ctx.moveTo(50.5, 10.5);
ctx.lineTo(150.5,10.5);
ctx.lineTo(50.5,110.5);
//ctx.lineTo(50,10);
ctx.closePath(); // use this as lineTo does not connect the start and end lines.
ctx.stroke();
ctx.beginPath();
ctx.moveTo(75 ,75);
ctx.lineTo(125, 75);
ctx.lineTo(125, 125);
ctx.lineTo(75, 75);
// you don't need to use closePath here as fill does not treat continuous path
// segments as special.
ctx.fill();
Note that when you use ctx.beginPath the method ctx.lineTo acts like ctx.moveTo if it is the first method called after the ctx.beginPath call. This can help reduce the complexity of rendering functions.
Correct use of ctx.closePath
It is a common mistake to think that ctx.closePath is related to ctx.beginPath. The function ctx.closePath is a path constructor function that adds to the current path by creating a line from the last added point to the last ctx.moveTo or first point after a ctx.beginPath call.
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
canvas.width = 400;
ctx.beginPath(); // begin a new path
ctx.beginPath();
ctx.moveTo(100.5, 10.5);
ctx.lineTo(150.5, 110.5);
ctx.lineTo(50.5, 110.5);
ctx.closePath(); // creates a line from (50,110) to (100,10)
ctx.moveTo(300.5, 10.5);
ctx.lineTo(350.5, 110.5);
ctx.lineTo(250.5, 110.5);
ctx.closePath(); // creates a line from (250,110) to (300,10)
ctx.stroke();
Why use ctx.closePath()
The ctx.closePath ensures that the ends of the shape are joined as a single path and will be rendered using ctx.lineJoin setting. If you don't use it then the path is open and the ctx.lineCap setting will be applied to the ends
The snippet shows the difference between ctx.closePath and using ctx.lineTo to create a line back to the start. Notice the join at the top of the triangle. The left one used ctx.closePath and the right used ctx.lineTo
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
canvas.width = 400;
ctx.lineWidth = 15;
ctx.lineJoin = "round";
ctx.lineCap = "butt";
ctx.beginPath();
ctx.moveTo(100,20);
ctx.lineTo(150,120);
ctx.lineTo(50,120);
ctx.closePath(); // creates a line from (50,210) to (100,20)
// closed path with the line joined at the start
ctx.stroke()
ctx.beginPath();
ctx.moveTo(250,20);
ctx.lineTo(300,120);
ctx.lineTo(200,120);
ctx.lineTo(250,20);
// path is open the last line back to the start is still open and
// will use ctx.lineCap to render the start and end
ctx.stroke()

Related

HTML Canvas : Fill Arc based complex shape

Below is a JSON structure which draws arcs with different start and end angle
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var elementDetail = {"element":[{"type":"ARC","x1":510,"y1":10,"x2":74.585653306513848,"y2":74.585653306514814,"r":1500,"xm":510,"ym":1510,"alpha":4.4178734952765524,"beta":4.71238898038469,"color":2},{"type":"ARC","x1":74.585653306514587,"y1":74.585653306514928,"x2":10,"y2":510,"r":1500,"xm":1510,"ym":510,"alpha":3.1415926535897931,"beta":3.43610813869793,"color":2},{"type":"ARC","x1":10,"y1":510,"x2":74.585653306514587,"y2":945.41434669348541,"r":1500,"xm":1510,"ym":510,"alpha":2.8470771684816563,"beta":3.1415926535897931,"color":2},{"type":"ARC","x1":74.585653306514871,"y1":945.41434669348541,"x2":510,"y2":1010,"r":1500,"xm":510,"ym":-490,"alpha":1.5707963267948966,"beta":1.8653118119030334,"color":2},{"type":"ARC","x1":510,"y1":1010,"x2":945.4143466934853,"y2":945.41434669348541,"r":1500,"xm":510,"ym":-490,"alpha":1.2762808416867597,"beta":1.5707963267948966,"color":2},{"type":"ARC","x1":945.41434669348541,"y1":945.41434669348541,"x2":1010,"y2":510,"r":1500,"xm":-490,"ym":510,"alpha":0,"beta":0.29451548510813697,"color":2},{"type":"ARC","x1":1010,"y1":510,"x2":945.41434669348519,"y2":74.585653306514132,"r":1500,"xm":-490,"ym":510,"alpha":5.9886698220714489,"beta":6.2831853071795862,"color":2},{"type":"ARC","x1":945.41434669348553,"y1":74.585653306514587,"x2":510,"y2":10,"r":1500,"xm":510,"ym":1510,"alpha":4.71238898038469,"beta":5.006904465492827,"color":2},{"type":"POINT","x1":510,"y1":510,"color":7}]}
ctx.beginPath();
elementDetail.element.map((elem, index) => {
ctx.arc(elem.xm, elem.ym, elem.r, elem.alpha, elem.beta);
})
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "#6fd0ff";
ctx.fill();
canvas{ zoom:.25}
<canvas id="myCanvas" width="1200" height="1200"></canvas>
The shape that I need to obtain is this:
I was able to loop through the json and fill draw stroke but not able to fill color inside the shape
I need help to fill the shape with color
Thanks
To avoid weird results, in complex shapes , I would rather perform the drawing in two steps :
Solid body rendering: draw and fill the shape
Outline rendering : draw the shape outter stroke
Note : When drawing arcs you need to know that ctx.arc() will create a line from the last coordinates of the current path, to the first position of the arc.
In order to avoid it, you need to call moveTo() to lift the drawing pen to the first position of the arc.
var elementDetail = {"element":[{"type":"ARC","x1":510,"y1":10,"x2":74.585653306513848,"y2":74.585653306514814,"r":1500,"xm":510,"ym":1510,"alpha":4.4178734952765524,"beta":4.71238898038469,"color":2},{"type":"ARC","x1":74.585653306514587,"y1":74.585653306514928,"x2":10,"y2":510,"r":1500,"xm":1510,"ym":510,"alpha":3.1415926535897931,"beta":3.43610813869793,"color":2},{"type":"ARC","x1":10,"y1":510,"x2":74.585653306514587,"y2":945.41434669348541,"r":1500,"xm":1510,"ym":510,"alpha":2.8470771684816563,"beta":3.1415926535897931,"color":2},{"type":"ARC","x1":74.585653306514871,"y1":945.41434669348541,"x2":510,"y2":1010,"r":1500,"xm":510,"ym":-490,"alpha":1.5707963267948966,"beta":1.8653118119030334,"color":2},{"type":"ARC","x1":510,"y1":1010,"x2":945.4143466934853,"y2":945.41434669348541,"r":1500,"xm":510,"ym":-490,"alpha":1.2762808416867597,"beta":1.5707963267948966,"color":2},{"type":"ARC","x1":945.41434669348541,"y1":945.41434669348541,"x2":1010,"y2":510,"r":1500,"xm":-490,"ym":510,"alpha":0,"beta":0.29451548510813697,"color":2},{"type":"ARC","x1":1010,"y1":510,"x2":945.41434669348519,"y2":74.585653306514132,"r":1500,"xm":-490,"ym":510,"alpha":5.9886698220714489,"beta":6.2831853071795862,"color":2},{"type":"ARC","x1":945.41434669348553,"y1":74.585653306514587,"x2":510,"y2":10,"r":1500,"xm":510,"ym":1510,"alpha":4.71238898038469,"beta":5.006904465492827,"color":2},{"type":"POINT","x1":510,"y1":510,"color":7}]}
let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d")
// draw the solid body
ctx.beginPath();
elementDetail.element.map((elem, index) => {
ctx.moveTo(0,0);
ctx.arc(elem.xm, elem.ym, elem.r, elem.alpha, elem.beta);
})
ctx.fillStyle = "#6fd0ff";
ctx.fill();
ctx.closePath();
// draw the outline
elementDetail.element.map((elem, index) => {
ctx.beginPath();
ctx.arc(elem.xm, elem.ym, elem.r, elem.alpha, elem.beta);
ctx.stroke();
})
ctx.closePath();
canvas{ zoom:.25}
<canvas id="myCanvas" width="1200" height="1200"></canvas>
Have you try to use context.fillStyle="your shape colour"?
I think you might use a path to build shape, so every time you start path, you need to assign a colour to every shape.
Try to draw arc and polygon seperately.
function drawGraphics(ctx){
ctx.beginPath();
ctx.fillStyle="#6fd0ff"
elementDetail.element.reverse().map((elem, index) => {
if(elem.type==="ARC"&&index<=8){
ctx.arc(elem.xm, elem.ym, elem.r, elem.alpha, elem.beta);
}
})
ctx.stroke()
ctx.fill();
ctx.closePath();
}

Hide a canvas ctx.rect() after 1 second - Javascript

I have a canvas which is filled with the webcam stream.
On top of that, I want to have rectangles (just the borders of a rectangle) appear for 1 second at random areas. So every second a rectangle will pop up, and the next it will be somewhere else.
Currently, rectangles are appearing every second but the last doesn't disappear. Therefore, on the 2nd second there are 2 rectangle, 3rd second 3 rectangles, etc...
I need to find a way to either have the rectangle appear for 1 second, have it removed after 1 second, or have it moved after 1 second: results are the same for me.
let sx; // x axis
let sy; // y axis
let i = setInterval( axisChanger, 1000 ); // pops up every second
function axisChanger() {
sx = getRandomInt(0, 535); // gets a random num
sy = getRandomInt(0, 445); // gets a random num
}
requestAnimationFrame(animate);
function animate(t) {
requestAnimationFrame(animate);
randomRect();
}
function randomRect() {
ctx.rect(sx, sy, 50, 30); // these 4 lines make a hollow rectangle: border only.
ctx.lineWidth = 2;
ctx.strokeStyle = '#FF0000';
ctx.stroke();
}
If I use clearRect(), then the inside of the rectangle will also be gone... and so part of the webcam stream with it.
If you only need to draw a single rectangle, replace rect() and stroke() with strokeRect():
function randomRect() {
ctx.lineWidth = 2;
ctx.strokeStyle = '#FF0000';
ctx.strokeRect(sx, sy, 50, 50);
}
The reason for the current behavior is that rect() adds to the main path and accumulates all rect() calls. Because of that the path must be cleared using beginPath().
But since you are only using a single rectangle you can simply use strokeRect() which does not add anything to the path but renders directly.
The alternative however, would be:
function randomRect() {
ctx.beginPath(); // clear path and sub-paths
ctx.rect(sx, sy, 50, 30); // these 4 lines make a hollow rectangle: border only.
ctx.lineWidth = 2;
ctx.strokeStyle = '#FF0000';
ctx.stroke();
}

Canvas special shape - animating

I'm finishing a project, but I have one more step to finish.
I want to visualize microphone input by a canvas.
Getting the data from the microphone isn't a problem.
But I want to visualize it in a special way. (see image)
I want to animate each element from the wave.
My problem isn't the animation.
My problem is to create those shapes in the CANVAS.
This is an example of one shape:
I can create a rounded corner shape with the canvas
const draw = () => {
fillRoundedRect(20, 20, 100, 100, 20);
ctx.fillStyle = "red";
ctx.fill();
};
const fillRoundedRect = (x, y, w, h, r) => {
ctx.beginPath();
ctx.moveTo(x+r, y);
ctx.lineTo(x+w-r, y);
ctx.quadraticCurveTo(x+w, y, x+w, y+r);
ctx.lineTo(x+w, y+h-r);
ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
ctx.lineTo(x+r, y+h);
ctx.quadraticCurveTo(x, y+h, x, y+h-r);
ctx.lineTo(x, y+r);
ctx.quadraticCurveTo(x, y, x+r, y);
ctx.fill();
};
Can someone help me with creating a shape like in the second image?
Thanks in advance guys!
Instead of trying to make a single shape with dependency on surrounding shapes and a high risk of headache math-wise, use instead two shapes which you merge using composition. My suggestion anyways.
Draw all the bars in full height using composition mode source-over (default)
Define a single shape on top using some sort of spline (I would suggest a cardinal spline).
Set composition mode to destination-out and render an enclosed shape using the spline as top "line".
Example
This should work in a loop (remember to clear canvas for each frame) but shows only the building stones needed here -
var ctx = c.getContext("2d");
var points = [];
var skippy = 0;
// render all bars
ctx.globalCompositeOperation = "source-over"; // not needed here, but in a loop yes!
// produce bars
ctx.beginPath(); // not needed here, but in a loop yes!
for(var x = 0; x < c.width; x += 30) {
ctx.rect(x, 0, 16, c.height)
// OKIDOKI, lets produce the spline using random points (y) as well
// but not for all, only every second for prettyness... modify to taste
if (skippy++ % 2 === 0) points.push(x, c.height * Math.random());
}
points.push(c.width, c.height * Math.random()); // one last
ctx.fillStyle = "rgb(198, 198, 198)";
ctx.fill();
// render spline
ctx.beginPath();
ctx.moveTo(0, c.height); // bottom left corner
curve(ctx, points); // spline
ctx.lineTo(c.width, c.height); // bottom right corner
ctx.closePath();
ctx.globalCompositeOperation = "destination-out";
ctx.fill();

Canvas - Creating a partially solid and partially dashed line

I'm trying to create a line in HTML canvas that is part solid and part dashed. Here is my code
ctx.moveTo(10,10);
ctx.lineTo(70,70);
ctx.setLineDash([5,15]);
ctx.lineTo(100,100);
ctx.stroke();
However this draws the entire line solid. I tried to add setLineDash before I drew the final line segment but that didn't work. Where did I go wrong?
setLineDash will apply for the whole path.
Either create a new Path by calling
//yourfirstLine();
ctx.stroke();
ctx.beginPath()
//yourSecondLine();
var ctx = c.getContext('2d')
ctx.beginPath()
ctx.moveTo(10,10);
ctx.lineTo(70,70);
ctx.stroke();
ctx.beginPath()
ctx.setLineDash([5,15]);
ctx.moveTo(80,80);
ctx.lineTo(100,100);
ctx.stroke();
<canvas id="c"></canvas>
or calculate the dashArray accordingly :
(here for the same result, it would be ctx.setLineDash([85,15,30])).
var ctx = c.getContext('2d')
ctx.beginPath()
ctx.setLineDash([85,15,30]);
ctx.moveTo(10,10);
ctx.lineTo(70,70);
ctx.lineTo(100,100);
ctx.stroke();
<canvas id="c"></canvas>
Personnaly, I would go for the former.
try adding this before your code:
ctx.setLineDash([1, 5]);

Canvas arc drawing strange shapes - coffeescript

#.ctx.lineWidth = 20
#.ctx.moveTo(i.x, i.y)
#.ctx.arc(i.x, i.y, 3, 0, Math.PI * 2)
Any reason why that code would make the image above?
I tried your version of the arc, and I find it difficult to understand what you are acctually asking. Therefore I made two versions, in order to visually show you what's happening.
You can look at them here!
UPDATED JSFIDDLE
http://jsfiddle.net/hqB6b/2/
HTML
First with the line inside.
<canvas id="ex" width="300" height="300">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
Second with NO line inside!
<canvas id="example" width="300" height="300">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
JS
var example = document.getElementById('example');
var ctx = example.getContext('2d');
var i = {x:100,
y:100}
ctx.strokeStyle = '#ff0000';
ctx.lineWidth = 1;
ctx.moveTo(i.x, i.y)
//HERE BEGINPATH IS USED AFTER MOVETO
ctx.beginPath();
ctx.arc(i.x, i.y, 50, 0, Math.PI * 2)
ctx.stroke();
var ex = document.getElementById('ex');
var ct = ex.getContext('2d');
var i = {x:100,
y:100}
ct.strokeStyle = '#ff0000';
ct.lineWidth = 1;
//HERE BEGINPATH IS USED BEFORE MOVETO
ct.beginPath();
ct.moveTo(i.x, i.y)
ct.arc(i.x, i.y, 50, 0, Math.PI * 2)
ct.stroke();
use beginPath before creating a path, and use closePath after creating it.
Since closePath... closes the path back to the first point, you might want stroke or fill before or after closing the path depending on what you seek.

Categories

Resources