SVG Masking

Like clipping paths, masks are used to hide and reveal parts of graphics and other elements. The difference is where clipping paths either show elements as fully opaque or fully transparent, masks allow you to control the degree of transparency or opacity. Where a clipping path leave every pixel as 0% or 100% opaque, masks can be 42% or 68% or any other percent opaque for any pixel in the mask.

The last few weeks, I’ve been looking at clipping paths, their attributes and some examples for how to work with them in SVG. Today I want to shift focus and talk about masks. You’ll find them to be similar in regards to the code you use to create them, but as you’ll see masks do different things than clipping paths and you’ll use each to create different effects.

What is Masking?

Any SVG object, graphic element, or group can be used as an alpha mask. That includes shapes, gradients, and patterns. The mask will determine what parts of and to what degree the SVG element(s) beneath the mask are visible.

Creating SVG masks is similar to creating clipping paths You define a mask inside a <mask> element, you give the mask an id, and then you reference the id on whatever element or group of elements you want the mask to apply.

Like clipping paths masks are never rendered directly. They’re only used as something that can be referenced. As with clipping paths I think masks will be easiest to understand and explain with examples so let’s dive right in.

Simple Examples

Here’s the same green circle I used through the clipping path posts. I used an SVG circle element, set it’s location via cx and cy and gave it a radius of 100px. The red outline shows the boundaries of the viewport.

1
2
3
<svg width="660" height="220" style="outline: 1px solid red">  
 <circle cx="110" cy="110" r="100" fill="#9c6" />  
</svg>

Here’s what the SVG looks like without any mask applied. It’s nothing particularly exciting, but it shows us where we are prior to adding an SVG mask.

Now let’s add a mask and see what happens.

I defined a mask inside <mask> tags. In this example the mask is a rectangle with equal height and width of 100px. Notice that unlike clipping paths, the mask has a fill, in this case a medium gray (#999). I also wrapped <defs> tags around the mask since the mask is being defined in one location and referenced in another.

1
2
3
4
5
6
7
8
9
<svg width="660" height="220" style="outline: 1px solid red">  
 <defs>  
   <mask id="mask-1">  
     <rect x="10" y="10" width="100" height="100" fill="#999" />  
   </mask>  
 </defs>

 <circle cx="110" cy="110" r="100" fill="#9c6" mask: url(#mask-1)"/>  
</svg>

The mask is referenced using the mask property, which is added to the element to be masked.

1
mask: url(“mask-1”)

If you followed along with the clipping path posts, the general set up for how this works should look very familiar. Here’s the result of adding the mask.

Note that I added a blue outline (not shown in the code) so we can see the boundaries of the mask.

There are two things to notice in this example. First is that anything outside the mask is clipped. Here the mask is 100px by 100px, so everything outside the square is clipped.

The second is that the value of the initial square changed. That’s because the mask is filled with a medium gray (#999). Nothing of the initial square would show through a black mask and 100% would show through a white mask. For everything in between the color and alpha channels of the mask are used to determine how much of the graphic shows through.

Here’s the result of using black (#000) as the fill for the mask. Everything else from the previous example is the same. Nothing now shows through the mask.

And here’s the result of using white (#fff) as the mask fill. Now everything shows through the mask, though the circle is still clipped outside the mask’s boundaries.

Using rgba() as Mask Fill

So far I’ve used hex values for the fill color. Another option is to use rgba(), which gives you direct control of the alpha channel or transparency.

In this example I set the fill to rgba(255,255,255,0.5625), which is white, but at 0.5625. As it happens 9/16 is 0.5625 so this is the same as setting the mask fill as #999 in hex.

1
2
3
4
5
6
7
8
9
<svg width="660" height="220" style="outline: 1px solid red">  
  <defs>  
    <mask id="mask-rgb-1">  
      <rect x="10" y="10" width="100" height="100" fill="rgba(255,255,255,0.5625)" />  
    </mask>  
  </defs>

   <circle cx="110" cy="110" r="100" fill="#9c6" mask="url(#mask-rgb-1)"/>  
 </svg>

If you compare the result of this mask to the one with #999 as a fill, they should look exactly the same.

Alternatively I could set the rgba() value as rgba(144, 144, 144, 1.0), leaving the alpha value as 1.0. Note: Technically #999 would be rgb(143.4375, 143.4375, 143.4375), but I figured rounding up was close enough for the example.

One last option would be to set the mask fill to white using hex or rgb() (#fff, 255,255,255) and then add an opacity (or fill-opacity) of 0.5625 (9÷16).

In all three examples the fill will be the same medium gray and it’s that fill that determines how much of the graphic shows through. I’ll skip the algorithm itself, since I don’t think it will help you work with masks or understand them any better, but you can read how the mask value at any point is computed here.

Closing Thoughts

If you understand how to work with SVG clipping paths, I think you’ll agree masks are easy to work with as well. You define the mask in one place and then you reference the mask on any element to which you want to apply it. The main difference is the mask fill which determines how much of the underlying element shows through the mask.

As you can no doubt guess, there’s more to masks than what I’ve shown here. Next week I want to talk about additional properties and attributes you can use with masks and talk about the difference between masks and mask content.

Like clipping paths, masks are coming to CSS, though the support isn’t quite there. However as I did with clipping paths, I’ll show you an example or two that shows how to work with masks in CSS. Then in the weeks that follow, I’ll show a variety of different examples to show that masks can be more than rectangles and they can be used to mask more than a simple circle.

Download a free sample from my book, Design Fundamentals.

Leave a Reply

Your email address will not be published. Required fields are marked *