Applying a Circular Crop Mask to an Image

Profile pictures are often masked by a circle. How can we present our images in such a circle. Lets explore two options, one where we use CSS and one where we actually mask the image data.

Applying the mask using CSS

Applying a circular mask to an image using CSS is quite straightforward. We only have to set the border-radius property and we’re done.

<style>
  img {
    border-radius: 50%;
  }
</style>

<img src="/media/cat.jpeg" alt="" />

But what if our image isn’t a square?

To prevent elliptic cats we have to make sure our image is a square.

<style>
  img {
    width: 128px;
    height: 128px;
    border-radius: 50%;
  }
</style>

<img src="/media/cat.jpeg" alt="" />

Okay, but now our cat is squished. Let’s unsquish it using CSS object-fit.

<style>
  img {
    width: 128px;
    height: 128px;
    object-fit: cover;
    border-radius: 50%;
  }
</style>

<img src="/media/cat.jpeg" alt="" />

Done!

Applying a Circular Mask using Canvas

To modify the actual image data we can use the <canvas> element and apply the circular mask to the image itself.

Let’s use a file input element and apply the mask to the added file.

<input type="file" accept="image/*" />

<canvas hidden></canvas>

<script>
  // get a reference to the file input
  const fileInput = document.querySelector("input");

  // get a reference to the output canvas
  const canvas = document.querySelector("canvas");

  // listen for the change event so we can capture the file
  fileInput.addEventListener("change", (e) => {
    // get a reference to the file
    const file = e.target.files[0];

    // let's load the image data
    const image = new Image();
    image.onload = () => {
      // use min size so we get a square
      const size = Math.min(image.naturalWidth, image.naturalHeight);

      // let's update the canvas size
      canvas.width = size;
      canvas.height = size;

      // draw image to canvas
      const ctx = canvas.getContext("2d");
      ctx.drawImage(image, 0, 0);

      // only draw image where mask is
      ctx.globalCompositeOperation = "destination-in";

      // draw our circle mask
      ctx.fillStyle = "#000";
      ctx.beginPath();
      ctx.arc(
        size * 0.5, // x
        size * 0.5, // y
        size * 0.5, // radius
        0, // start angle
        2 * Math.PI // end angle
      );
      ctx.fill();

      // restore to default composite operation (is draw over current image)
      ctx.globalCompositeOperation = "source-over";

      // show canvas
      canvas.hidden = false;
    };
    image.src = URL.createObjectURL(file);
  });
</script>

Try it below!

I use Twitter to share new webdevelopment tips and tricks, so Follow me there if you found this interesting and want to learn more.

Rik Schennink

Indie Product Developer

to pqina.nl