Jonatan Stenbacka
Jonatan Stenbacka

Reputation: 1864

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:

http://i57.tinypic.com/63tx02.png

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.

Upvotes: 0

Views: 558

Answers (2)

user1693593
user1693593

Reputation:

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.

Upvotes: 1

user2782001
user2782001

Reputation: 3488

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/

Upvotes: 0

Related Questions