HTML5 Canvas: gradients and strokeStyle have me confused - javascript

Why does the following code not produce three lines, all with similar gradients?
<html>
<body style="background: black;">
<canvas id="Test" width="516" height="404"> </canvas>
<script>
var ctx = document.getElementById('Test').getContext('2d');
ctx.lineWidth = 8;
function addColorStops(gradient) {
gradient.addColorStop(0.5, 'rgba(151, 165, 193, 0.5)');
gradient.addColorStop(1, 'rgba(151, 165, 193, 1)');
}
function drawLine(x1, x2, y) {
var g = ctx.createLinearGradient(x1, y, x2, y);
addColorStops(g);
ctx.strokeStyle = g;
ctx.moveTo(x1, y);
ctx.lineTo(x2, y);
ctx.stroke();
}
drawLine(10, 100, 10);
drawLine(10, 100, 30);
drawLine(10, 100, 50);
</script>
</body>
</html>
Instead the first line gets a very very slight gradient, the second line gets a pretty good gradient, and the last gets a drastic gradient.
I think this stems from misunderstanding of either how the parameters to createLinearGradient are supposed to work, or misunderstanding how strokeStyle assignments influence future strokes...

Agh, I figured it out. I need to add a ctx.beginPath() right before the ctx.strokeStyle = g;. It turns out that lines are part of a path and thus if you don't begin a new path it'll think you're still continuing the old one, and thus use your original start point and final end point as the start and end for gradient purposes.

I was getting the strokeStyle overridden! By adding a beginPath my stroke colors work..
ctx.beginPath();
ctx.moveTo( x, y );
ctx.lineTo( x, y );
ctx.strokeStyle = "#182945";
ctx.stroke();
Thanks

Related

setLineDash([3,2]), draws a Solid Line instead of dashed lines. (Canvas HTML5)

I'm trying to draw different rectangular designs using setLineDash and lineDashOffset(HTML5 Canvas-2d Context). But Solid lines are drawn instead of dashed lines. Please help.
Points to be noted:
Canvas Scaling is done.
drawLine API is used for drawing lines.
I'm using different integer points to draw lines.
setLineDash([3,2]). Strictly, dashed thickness is "3" and space between them is "2" should be used.
`
this.context.setLineDash([3,2]);
this.context.lineDashOffset = 2;
drawLine(300,9.5,570,10);
drawLine(300,9.5,300,100);
drawLine(300,99.5,570,100);
drawLine(570,9.5,570,100);`
My Complete code for your reference:
Codepen Link
Inspired from:
Document Link
This happens because you close the path using closePath().
This method doesn't say "the path declaration is over", it says, "make my path an enclosed path", which means it will lineTo the last entrance point in the path. Doing so and depending on the line's length, the second line may have its own dashes cover the holes of the first line.
const context = canvas.getContext("2d");
function drawLine(x1, y1, x2, y2, mode) {
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
switch (mode) {
case "closePath":
context.closePath();
case "lineTo":
context.lineTo(x1, y1);
}
context.stroke();
}
context.lineWidth = 2;
context.setLineDash([3, 2]);
context.strokeStyle = "red";
drawLine(30, 9.5, 30, 100, "closePath");
context.strokeStyle = "blue";
drawLine(60, 9.5, 60, 100, "lineTo");
context.strokeStyle = "green";
drawLine(90, 9.5, 90, 100, "");
<canvas id="canvas"></canvas>
And to avoid this, don't call closePath().

Custom diamond shape in html or jscript

I need is some way to draw a shape in html. I've tried searching around, but I haven't found anything related.
Here is a sketch of what I need.
I need to draw shape in red. I know how to draw the dots, but I don't know how to connect them, because the red dots can move. The blue dots mark where the red dots can go.
You could use a canvas. Here’s how you could draw a triangle:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.moveTo(50, 50);
ctx.lineTo(100, 50);
ctx.lineTo(50, 100);
ctx.closePath();
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
ctx.fill();
ctx.strokeStyle = 'blue';
ctx.lineWidth = 3;
ctx.stroke();
<canvas id="canvas" width="150" height="150">Your browser lacks canvas support.</canvas>
I’ll leave it to you to figure out how to draw a diamond and make it red.

Do I have to have the content.beginPath() and content.closePath()?

Does the beginPath and the closePath have to be included for this line to draw or for all of the graphics. I have the new HTML 5 Canvas book but this I was not completely certain of. I commented out the two lines and the line still displayed. What is the significance of these two lines.
Question: What does the beginPath() and closePath() do?
Code:
context.lineJoin='round';
context.lineCap='butt';
context.beginPath();
context.moveTo(10, 100);
context.lineTo(35, 100);
context.lineTo(35,125);
context.stroke();
context.closePath();
No, beginPath and closePath are not necessary.
A canvas context has a current path. You can add instructions to that path with methods such as moveTo and lineTo, among others. When you're done constructing the path, you can use methods such as stroke and fill, which draw on the canvas using the current path.
closePath is just another instruction you can add. You may not notice its effect when using fill, but when using stroke, it will (essentially) do a line back to the starting position, ‘closing’ the path. Compare and contrast:
ctx.moveTo(10, 10); ctx.moveTo(10, 10);
ctx.lineTo(90, 10); ctx.lineTo(90, 10);
ctx.lineTo(90, 90); ctx.lineTo(90, 90);
ctx.closePath();
ctx.stroke(); ctx.stroke();
beginPath, on the other hand, discards the previous path and lets you start a new one. Without it, you'd be appending more and more to the previous path, which may be undesirable. Compare and contrast:
ctx.moveTo(10, 10); ctx.moveTo(10, 10);
ctx.lineTo(90, 10); ctx.lineTo(90, 10);
ctx.lineWidth = 4; ctx.lineWidth = 4;
ctx.strokeStyle = "red"; ctx.strokeStyle = "red";
ctx.stroke(); ctx.stroke();
ctx.beginPath();
ctx.moveTo(10, 20); ctx.moveTo(10, 20);
ctx.lineTo(90, 20); ctx.lineTo(90, 20);
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.strokeStyle = "blue"; ctx.strokeStyle = "blue";
ctx.stroke(); ctx.stroke();
beginPath() clears the old path so you can define a new one.
closePath() connects the first point with the last point and is not needed in your example. In any case it would have be used before stroking of filling to have an effect on the rasterized result.

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/

How to prevent a path from being filled (again)?

I am drawing some shapes on a canvas element. The first element is a path, which should not get filled at all. I know I can set fillStyle to none, but it gets filled twice.
Here is some example code (also on jsfiddle):
can = document.getElementById('can');
ctx = can.getContext('2d');
function drawPoint(x,y){
ctx.arc(x,y,12,0,Math.PI*2,false);
ctx.fillStyle ='rgba(255, 0, 0, 0.2)';
ctx.fill();
}
function shape(){
ctx.fillStyle = 'rgba(0,255,0,0.2)';
ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(100,30);
ctx.lineTo(30,200)
ctx.closePath();
ctx.stroke();
ctx.fill();
}
shape();
drawPoint(30,12);
This is just an example code, to illustrate the problem I am facing.
When I draw the shape afterwards, the point is in the background. So this won't work. I also searched for resources on how the fill method works but couldn't find anything useful.
So how can I draw the shape without filling it?
Just don't call fill()...
For example, if you want your method reusable you can use a flag:
function shape(fill){
fill = fill || false;
ctx.fillStyle = 'rgba(0,255,0,0.2)';
ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(100,30);
ctx.lineTo(30,200)
ctx.closePath();
ctx.stroke();
if (fill) ctx.fill();
}
Now you can call:
draw(); /// won't fill
draw(true); /// fills
Also add a beginPath() to this method or else it will just add to the path of the first shape (which perhaps is what you mean?):
function drawPoint(x,y){
ctx.beginPath();
ctx.arc(x,y,12,0,Math.PI*2,false);
ctx.fillStyle ='rgba(255, 0, 0, 0.2)';
ctx.fill();
}
Hope this helps (and that I didn't misunderstand your question)!
Modified fiddle here

Categories