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();
}
Related
I been stuck on getting the waves to look just like I want. I'm trying to figure out how to get the base of the wave to be the color I need it. I can do my desired color but it blocks the background. I can not see anything behind it because I was using like a reflection. Maybe someone can figure it out cause I'm having difficulties getting it to work... I plan on making the wave drop and rise. Here is a link to the code pen: HERE
Here is where I have the vertical reflection:
var x = $.cx - $.length / 2 + $.length / $.count * i,
y = height + $.simplex.noise2D($.xoff, $.yoff) * amp + sway;
$.ctx[i === 0 ? 'moveTo' : 'lineTo'](x, y);
}
$.ctx.lineTo($.w, $.h); // -$.h - Vertically reflection
$.ctx.lineTo(0, $.h); // -$.h - Vertically reflection
$.ctx.closePath();
$.ctx.fillStyle = color;
if (comp) {
$.ctx.globalCompositeOperation = comp;
}
$.ctx.fill();
My desired look for the waves is below:
Here is what I got with a successful transparent top, just not the right coloring:
Your problem is that the screen blending of the three colors generates a solid white color, so all the bottom of your canvas becomes white.
Here I simplified a lot the situation, with just 3 rectangles. Your bottom of canvas is my central white square:
const c2 = canvas.cloneNode();
const ctx = canvas.getContext("2d");
ctx.globalCompositeOperation = 'screen';
ctx.fillStyle = '#fb0000';
ctx.fillRect(0,0,50,50);
ctx.fillStyle = "#00ff8e";
ctx.fillRect(12,12,50,50);
ctx.fillStyle = "#6F33FF";
ctx.fillRect(25,25,50,50);
body {
background: #CCC;
}
<canvas id="canvas"></canvas>
So what we need, is a way to make this central square transparent so that we can draw our background behind.
To do this, we will need to draw our shapes at least two times:
once in normal compositing mode, so that we get the full overlap.
once again as source-in compositing mode, so that we get only where all our shapes do overlap.
const ctx = canvas.getContext("2d");
function drawShapes(mode) {
ctx.globalCompositeOperation = mode;
ctx.fillStyle = '#fb0000';
ctx.fillRect(0,0,50,50);
ctx.fillStyle = "#00ff8e";
ctx.fillRect(12,12,50,50);
ctx.fillStyle = "#6F33FF";
ctx.fillRect(25,25,50,50);
}
drawShapes('screen');
drawShapes('source-in');
body {
background: #CCC;
}
<canvas id="canvas"></canvas>
Now we have our overlapping area, we will be able to use it as a cutting shape in a third operation. But to do it, we will need a second, off-screen canvas to perform the compositing of the two states:
const c2 = canvas.cloneNode();
const ctx = canvas.getContext("2d");
const ctx2 = c2.getContext("2d");
function drawShapes(ctx, comp) {
ctx.globalCompositeOperation = comp;
ctx.fillStyle = '#fb0000';
ctx.fillRect(0, 0, 50, 50);
ctx.fillStyle = "#00ff8e";
ctx.fillRect(12, 12, 50, 50);
ctx.fillStyle = "#6F33FF";
ctx.fillRect(25, 25, 50, 50);
}
// first draw our screen, with unwanted white square
drawShapes(ctx, 'screen');
// draw it on the offscreen canvas
ctx2.drawImage(ctx.canvas, 0, 0)
// draw the shapes once again on the offscreen canvas to get the cutting shape
drawShapes(ctx2, 'source-in');
// cut the visible canvas
ctx.globalCompositeOperation = 'destination-out'
ctx.drawImage(ctx2.canvas, 0, 0);
body {
background: #CCC
}
<canvas id="canvas"></canvas>
And voilĂ , our white square is now transparent, we can draw whatever we want behind our scene using the destination-over composite operation.
How do I create a transparent gradient stroke that using html5 canvas? I need it to go from one point to another and look like the below image.
At the moment I have got this:
const gradient = ctx.createLinearGradient(1, 0, 100, 0);
gradient.addColorStop(0, '#fff');
gradient.addColorStop(1, '#d29baf');
ctx.lineWidth = 30;
ctx.strokeStyle = gradient;
ctx.beginPath();
ctx.moveTo(fromXPos, fromYPos);
ctx.lineTo(toXPos, toYPos);
ctx.stroke();
This makes it look like a solid block though like:
Thanks.
Fill a shape
Use a shape and fill it with the gradient.
You can use CSS colour type rgba(red,green,blue,alpha) where red,green,blue are values from 0-255 and alpha is 0 transparent to 1 opaque.
To create a shape you start with ctx.beginPath() to create a new shape then use lineTo(x,y) to mark out each corner. If you want to add another shape using the same fill or stroke you use ctx.moveTo(x,y) to move to the first point.
Note many people use ctx.beginPath(); ctx.moveTo(x,y); but that works just the same as ctx.beginPath(); ctx.lineTo(x,y); As the first point after beginPath is always converted to a moveTo for any type of path object.
const ctx = canvas.getContext("2d");
// draw first box (left of canvas)
ctx.fillStyle = "#ab7383";
ctx.fillRect(20,100,50,50);
// draw second box (to right of first)
ctx.fillStyle = "#904860";
ctx.fillRect(100,20,50,130);
// gradient from top of second box to bottom of both boxes
const g = ctx.createLinearGradient(0, 20, 0, 150);
g.addColorStop(0, `rgba(${0xd2},${0xba},${0xaf},1`); // opaque
g.addColorStop(1, `rgba(${0xd2},${0xba},${0xaf},0`); // transparent
ctx.fillStyle = g;
ctx.beginPath();
ctx.lineTo(70, 100); // top right of first box
ctx.lineTo(100, 20); // top left of second box
ctx.lineTo(100, 150); // bottom left of second box
ctx.lineTo(70, 150); // bottom right of first box
ctx.fill(); // fill the shape
<canvas id="canvas" style="border:2px solid black"></canvas>
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()
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]);
How I clean text from canvas shape ?
this my code:
<div id="ways" style="width:1000px;margin:0 auto;height:100%;">
<canvas id="canvas" width="1000" height="1000"></canvas>
and fiddle
Just refactor the code a bit -
Extract the lines which draws the circle into a single function which takes one of those circle objects as an argument:
function drawCircle(circle) {
context.beginPath();
context.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
context.lineWidth = lineWidth;
context.strokeStyle = '#003300';
context.stroke();
}
Then replace those lines which they came from originally, in the loop with this line (after the circle object has been pushed):
drawCircle(circles[circles.length-1]); // draw last added circle
Now you can use this function in your click events by modifying the code (here, it toggles text on and off):
if (context.isPointInPath(x, y)) {
if (circle.clicked) {
drawCircle(circle); // now we can reuse this function to clear the text
circle.clicked = false;
}
else {
circle.clicked = true;
context.fillStyle = "blue";
context.font = "bold 34px Arial";
context.textAlign="center";
context.fillText("Yeah", circle.x, circle.y);
}
break;
}
Modified fiddle
Just a note with this approach: this will redraw the circles on top of each other. Over time the lines will start to look jaggy due to the added anti-aliasing on the edges. For this reason a full clear of the canvas is better, and then redraw them all. But I'll leave that as an exercise for you.. :-)