HTML Canvas fill() Method: Filling Paths and Shapes

The HTML Canvas fill() method is a fundamental function in the Canvas API, used to fill the interior of paths and shapes with the current fill style, which could be a color, gradient, or pattern. This method is crucial for creating visually appealing and complex graphics on the canvas. This guide will delve into the specifics of the fill() method, including its syntax, practical usage, and important considerations.

What is the fill() Method?

The fill() method is an essential tool for rendering graphics on an HTML canvas. It takes a defined path—created using methods like moveTo(), lineTo(), arc(), and quadraticCurveTo(), and others—and fills the area enclosed by that path using the current fill style. This fill style is determined by the fillStyle property, which can be set to a color, gradient, or pattern.

Purpose of the fill() Method

The primary purpose of the fill() method is to:

  • Color Shapes: Render filled shapes, such as rectangles, circles, and polygons.
  • Enhance Graphics: Add visual depth and appeal to canvas drawings.
  • Create Complex Designs: Combine multiple paths to form intricate shapes and patterns.
  • Add Styling: Use gradients and patterns to create unique visual effects.

Syntax of the fill() Method

The fill() method has the following syntax:

ctx.fill(fillRule);

Where:

  • ctx is the 2D rendering context of the canvas, obtained via canvas.getContext('2d').
  • fillRule (Optional): A string specifying how to determine whether a point is inside a path. Possible values are:
    • "nonzero" (default): The non-zero winding rule determines the fill region.
    • "evenodd": The even-odd rule determines the fill region.

Important Attributes

The fill() method, while simple in its call, interacts with various other context properties. Here’s a breakdown of the key related properties:

Property Type Description
fillStyle String, CanvasGradient, CanvasPattern Specifies the color, gradient, or pattern used to fill shapes. Can be a color name, a hexadecimal code, an rgb() or rgba() value, or a CanvasGradient or CanvasPattern object.
beginPath() Function Starts a new path. Call this method before defining a new path to avoid unexpected interactions with previous drawings.
moveTo(x, y) Function Moves the starting point of a new subpath to the coordinates (x, y).
lineTo(x, y) Function Draws a line from the current drawing point to the coordinates (x, y).
arc(x, y, radius, startAngle, endAngle, anticlockwise) Function Adds an arc to the path with center at (x, y), radius, start angle, and end angle.
quadraticCurveTo(cpx, cpy, x, y) Function Adds a quadratic Bézier curve to the path. Requires control point (cpx, cpy) and end point (x, y).
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) Function Adds a cubic Bézier curve to the path. Requires two control points (cp1x, cp1y), (cp2x, cp2y) and end point (x, y).
closePath() Function Closes the current subpath by drawing a straight line from the current position back to the starting point.

Note: Always define a path using beginPath() and closing it with closePath() when needed before applying the fill. This practice prevents unexpected interactions between shapes. ⚠️

Basic Usage of the fill() Method

Let's explore how to use the fill() method with practical examples.

Filling a Simple Rectangle

First, we'll draw a rectangle using rect() and fill it with a color using fill() method. Note that rect() creates a closed rectangle path, making it directly fillable by fill()

<canvas id="canvasRectFill" width="200" height="150" style="border: 1px solid black;"></canvas>

<script>
//<![CDATA[

  const canvas = document.getElementById("canvasRectFill");
  const ctx = canvas.getContext("2d");

  ctx.fillStyle = "skyblue";
  ctx.beginPath();
  ctx.rect(20, 20, 160, 100);
  ctx.fill();

//]]]]><![CDATA[>
</script>

Filling a Triangle

Next, we’ll fill a triangle by defining its path with moveTo() and lineTo() methods and the fill() method.

<canvas id="canvasTriangleFill" width="200" height="150" style="border: 1px solid black;"></canvas>

<script>
//<![CDATA[

  const canvas_triangle = document.getElementById("canvasTriangleFill");
  const ctx_triangle = canvas_triangle.getContext("2d");

  ctx_triangle.fillStyle = "lightgreen";
  ctx_triangle.beginPath();
  ctx_triangle.moveTo(50, 20);
  ctx_triangle.lineTo(150, 20);
  ctx_triangle.lineTo(100, 120);
  ctx_triangle.closePath();
  ctx_triangle.fill();

//]]]]><![CDATA[>
</script>

Filling a Circle (Arc)

Here we’ll create and fill a circle (arc) using the arc() method.

<canvas id="canvasCircleFill" width="200" height="150" style="border: 1px solid black;"></canvas>

<script>
//<![CDATA[

  const canvas_circle = document.getElementById("canvasCircleFill");
  const ctx_circle = canvas_circle.getContext("2d");

  ctx_circle.fillStyle = "lightcoral";
  ctx_circle.beginPath();
  ctx_circle.arc(100, 75, 50, 0, 2 * Math.PI);
  ctx_circle.fill();

//]]]]><![CDATA[>
</script>

Filling a Complex Shape

Now we’ll create a more complex path using moveTo(), lineTo(), and quadraticCurveTo() methods.

<canvas id="canvasComplexFill" width="250" height="200" style="border: 1px solid black;"></canvas>

<script>
//<![CDATA[

  const canvas_complex = document.getElementById("canvasComplexFill");
  const ctx_complex = canvas_complex.getContext("2d");

  ctx_complex.fillStyle = "orange";
  ctx_complex.beginPath();
  ctx_complex.moveTo(50, 50);
  ctx_complex.lineTo(150, 50);
  ctx_complex.quadraticCurveTo(200, 100, 150, 150);
  ctx_complex.lineTo(50, 150);
  ctx_complex.closePath();
  ctx_complex.fill();

//]]]]><![CDATA[>
</script>

Understanding fillRule

The fillRule parameter allows you to control how overlapping paths are filled. Let's demonstrate this by drawing two overlapping rectangles and using different fill rules.

Non-zero winding rule (default)

<canvas id="canvasNonZeroFill" width="250" height="200" style="border: 1px solid black;"></canvas>

<script>
//<![CDATA[

    const canvas_nonzero = document.getElementById('canvasNonZeroFill');
    const ctx_nonzero = canvas_nonzero.getContext('2d');

    ctx_nonzero.fillStyle = 'teal';
    ctx_nonzero.beginPath();
    ctx_nonzero.rect(20, 20, 120, 120);
    ctx_nonzero.rect(80, 20, 120, 120);
    ctx_nonzero.fill();

//]]]]><![CDATA[>
</script>

Even-odd rule

<canvas id="canvasEvenOddFill" width="250" height="200" style="border: 1px solid black;"></canvas>

<script>
//<![CDATA[

 const canvas_evenodd = document.getElementById('canvasEvenOddFill');
    const ctx_evenodd = canvas_evenodd.getContext('2d');

    ctx_evenodd.fillStyle = 'teal';
    ctx_evenodd.beginPath();
    ctx_evenodd.rect(20, 20, 120, 120);
    ctx_evenodd.rect(80, 20, 120, 120);
    ctx_evenodd.fill('evenodd');

//]]]]><![CDATA[>
</script>

In the non-zero winding rule, the overlapping region of the rectangles is still filled, while in the even-odd rule, this region is not filled, creating a "hole". This occurs because, with the even-odd rule, a point is considered inside the shape if a ray drawn from that point to infinity crosses the path an odd number of times. If the ray crosses an even number of times, the point is considered outside and not filled.

Note: Understanding fillRule is crucial when working with complex overlapping paths, especially if you need to create shapes with holes or specific fill behaviors.💡

Real-World Application: Creating a Simple Star

Let's use the fill() method to create a star shape, illustrating its use in slightly more complex graphics.

<canvas id="canvasStarFill" width="200" height="200" style="border: 1px solid black;"></canvas>

<script>
//<![CDATA[

    const canvas_star = document.getElementById('canvasStarFill');
    const ctx_star = canvas_star.getContext('2d');

    function drawStar(ctx, x, y, spikes, outerRadius, innerRadius, color) {
        let rot = Math.PI / 2 * 3;
        let step = Math.PI / spikes;

        ctx.beginPath();
        ctx.moveTo(x, y - outerRadius);
        for (let i = 0; i < spikes; i++) {
            let x_outer = x + Math.cos(rot) * outerRadius;
            let y_outer = y + Math.sin(rot) * outerRadius;
            ctx.lineTo(x_outer, y_outer);
            rot += step;

            let x_inner = x + Math.cos(rot) * innerRadius;
            let y_inner = y + Math.sin(rot) * innerRadius;
            ctx.lineTo(x_inner, y_inner);
            rot += step;
        }
        ctx.closePath();
        ctx.fillStyle = color;
        ctx.fill();
    }

    drawStar(ctx_star, 100, 100, 5, 70, 35, 'gold');

//]]]]><![CDATA[>
</script>

This example demonstrates how to use moveTo(), lineTo(), and fill() in combination to draw more intricate shapes. By manipulating the path definitions, you can create a vast variety of forms.

Browser Support

The fill() method is well-supported across all modern browsers, ensuring consistent rendering across various platforms.

Conclusion

The fill() method is a cornerstone of the HTML Canvas API, essential for rendering filled shapes and paths. By understanding its interaction with properties like fillStyle and fillRule, and its integration with path definition methods, you can create diverse and complex graphics on your canvas. This guide provides a solid foundation for using fill() effectively in your web development projects. 🎨