Scalable Vector Graphics, or SVG, are one of those front-end tools that can open up a world of creative ideas. I've been playing around with it more, for work and to distract myself from my hollow, quarantined soul. I did both and made my first component with some cutesy SVG animation.
But near the end, I hit a snag with how SVG can hide parts of the image. Sometimes I wanted to show specific parts of an image. Other times I wanted to hide them. I couldn't get this effect for the sun and moon animations, so I got it with border and fill colors. That's not ideal, but I can always fix it up later.
But while I wanted to figure this out, I had a hard time finding a clear explanation of showing and hiding SVG. So I wrote one myself! The two parts of SVG one needs for these effects are
mask. Here I'll break down the basics of how to use them with SVG to hide shapes, images, and if you're lucky, painful tangles of repressed emotion.
Let's say I have a red rectangle, but I only want to show a small circle within it. That's when I need
clip-path is an SVG shape you pair with a second. Anything in the second shape that doesn't overlap with the
clip-path shape gets cropped out, lost, and never seen again. You destroyed it and I hope you're happy.
Ahem...anyways, here's an example. Let's start with this red rectangle.
<svg width="400" height="200" viewbox="-200 -100 400 200"> <rect x="-200" y="-100" width="400" height="200" fill="red" /> </svg>
Let's add a
clip-path to this thing. First, I need to define the circle it'll be using. It's positioned to be in the rectangle's center. But I need to define it within a
clip-path. This has to go within a
defs element, which is SVG's way of storing variables. Anything in
defs won't directly render but can then affect other elements, like repressed memories of staring wistfully at an empty street after the third hard cider.
So within the
defs element, the
circle must go in a
clipPath element. It also needs a unique ID for reference.
<defs> <clipPath id="clip-path"> <circle cx="0" cy="0" r="50" /> </clipPath> </defs>
clip-path circle can be used with the red rectangle. It needs the
clip-path attribute with the unique ID, which looks like
clip-path="url(#clip-path)", and it's good to go.
<svg width="400" height="200" viewbox="-200 -100 400 200"> <defs> <clipPath id="clip-path"> <circle cx="0" cy="0" r="50" /> </clipPath> </defs> <rect clip-path="url(#clip-path)" x="-200" y="-100" width="400" height="200" fill="red" /> </svg>
You can see the final result below. The light yellow area is where the rest of the rectangle is being hidden below the clip-path.
Once I finally peeled myself off the couch and found the will to keep working despite the terror of it all, I asked, "What if I want the reverse result? If I want to hide everything except that red dot?" To do that, we need a
mask. But this approach is a little more complex.
clip-path, I only had to define a visible area. With
mask, there are two steps:
- Define a visible area. Anything outside it gets cropped out.
- Define what areas within the visible area are also cropped out.
This is like a
clip-path but with a lot more potential for what to show and hide, but will only realize once it can experience regular human interaction again. But I only needed these lonely basics to the desired result. If you want to see what it can do, including using shades of gray for translucent effects, check out this blog post of mask examples.
First, defining a visible area here is easy: I want the rectangle to be visible! I'm hiding space within the rectangle, so the entire rectangle itself should be my visible area. I define this within a
mask element, which can also go in the
<defs> <mask id="mask"> <rect x="-200" y="-100" width="400" height="200" fill="white" /> </mask> </defs>
fill="white" I added to the shape. That's important because, in a mask, the visible area must have a white fill color. Don't you dare deprive it of what it needs. You monster.
Now I need to decide what area to hide within the visible area. This is the circle in the middle of the rectangle. This time it needs
fill="black" to hide it.
<defs> <mask id="mask"> <rect x="-200" y="-100" width="400" height="200" fill="white" /> <circle cx="0" cy="0" r="50" fill="black" /> </mask> </defs>
With this basic mask complete, we can connect it to the red rectangle. It's done the same way as
clip-path but with the
<svg width="400" height="200" viewbox="-200 -100 400 200"> <defs> <mask id="mask"> <rect x="-200" y="-100" width="400" height="200" fill="white" /> <circle cx="0" cy="0" r="50" fill="black" /> </mask> </defs> <rect mask="url(#mask)" fill="red" x="-200" y="-100" width="400" height="200" /> </svg>
This gives me what I want: a full rectangle missing the center circle. The result is below with the light yellow again showing what's hidden behind the mask. The SVG mask anyway, not the mask of positivity hiding the sense that nothing will truly be okay again.
Not that mask. The SVG mask.
My written example is redundant. It defines the same rectangle shape twice, but with different fills and masks. SVG lets you define a shape once and use it elsewhere, like a variable reference. I did that in my CodePen example - check the code and see how that works!
I've still got plenty to learn with SVG, but
mask are two foundations I'm sure will help me make cooler stuff. If you're like me and feel jealous of the cool, graphic stuff you see developers making on CodePen, SVG is one of the first steps to making similar work and assuaging the deep desolation fermenting within one's soul.
Rereading this post, I think the second part may just be me using learning to handle the mental stress of extended quarantine. But the point about writing better code still stands.
So focus on that. I need to take a long walk. To the wine shop.