Last week I showed you the very basics of SVG clipping paths. I mentioned the difference between clipping and masking and then worked through a simple example to show you SVG clipping paths in action. I closed by showing how to do the same thing using the CSS clip-path property.
As you might guess, there’s more to SVG clipping than what I talked about last week. Today I want to dig a little deeper into the basics. I want to talk about the different attributes you can add to the clipPath element to give you more control over your clipping paths.
First, though, I want to talk about the initial clipping path.
The Initial Clipping Path
The SVG canvas is infinite. You can create a rectangle and position it 4 trillion pixels to the left and SVG is fine with that. My laptop monitor has a lot less than 4 trillion pixels, but the SVG canvas is theoretically infinite in size. The viewport functions as a clipping path for the canvas.
If you’ve read any of my previous SVG posts, you’ve likely noticed I add an outline around the SVG viewport in most examples. The reason is to show you its boundaries and to show that when all or part of an element moves outside of the viewport, it’s no longer visible.
The root SVG element operates according to CSS layout rules. It’s a block level element and you can position it where you want in your document. You can style it like you would other block level elements. For example you could apply margins to the SVG element or float it left or right if you want.
Another property you can set on the SVG element is the overflow property. The default value of overflow is usually visible, but in the case of SVG elements, the default is hidden, which means the SVG element clips anything outside it’s boundaries. This is the initial clipping path.
Other SVG elements like marker and pattern establish new viewports within the root SVG viewport. These too have overflow set to hidden by default creating an initial clipping path for the element.
You can change the default behavior in a couple of ways. One is to explicitly set the overflow property to visible allowing what’s both inside and outside the viewport to display.
Here’s an example where I positioned an SVG rectangle’s top left corner at –50px –25px so that some of it sits outside the viewport.
<svg width="600" height="300" style="outline: 1px solid red;">
<rect x="-50" y="-25" width="200" height="100" fill="#9c6" />
By default the overflow is hidden and some of the rectangle is clipped.
Changing the overflow from hidden to visible allows the part of the rectangle that’s been clipped to become visible.
<svg width="600" height="300" style="outline: 5px solid #630; overflow: visible">
<rect x="-50" y="-25" width="200" height="100" fill="red" />
The only difference between this and the previous example is that I’ve set the overflow property to visible. The clipped portion of the rectangle can now be seen, though outside of the viewport boundaries.
I can’t think of too many reasons why you’d want to do this with the root SVG viewport, but you can do it. The only reason I’ve ever done it is in examples like this when I want to show the effects of setting overflow: visible.
The other way to change the initial clipping path is to use the CSS clip-path property on the SVG element. As I mentioned last week, the CSS clip-path property doesn’t have great support at the moment. I bring it up now mostly to let you know you can change the default behavior of a rectangular viewport.
Here I changed the clip-path of the root SVG element to be a circle instead of the default rectangle. Inside the root SVG I added a rectangle.
<svg width="660" height="220" style="outline: 1px solid #f00; clip-path: circle(100px at 50px 50px);">
<rect x="25" y="25" width="200" height="100" fill="#9c6" />
The result is the entire viewport is clipped to the shape of a circle. Even the outline I added to show the boundaries of the viewport is clipped.
Since the code may or may not work for you depending on the browser you’re currently using, here’s an image of the result in case the above SVG isn’t displaying correctly.
There is a use case for changing the initial clipping path. If you’ve created a viewBox, especially one with the preserveAspectRatio set, you might prefer the initial clipping path to match the viewBox as opposed to the viewport. If that’s the case you would want to set the clip-path to the dimensions and coordinates of the viewBox instead of the default which will match the viewport.
One of the things you may have noticed about working with SVG is there are different coordinate systems that might be in use at any given time. You might be working with the coordinate system of the SVG canvas, the viewport, the viewBox, and yes, even the clipping path.
You define the coordinate system for the clipping path using the clipPathUnits attribute. It’s value can be either userSpaceOnUse or objectBoundingBox
The value userSpaceOnUse is the default. It’s the same as not setting the clipPathUnits at all so it’s the value that’s been in effect in the examples last week and so far in this post. It represents values in the current user coordinate system when the clipPath element is referenced. That will usually be the coordinate system for the viewport.
The value objectBoundingBox establishes the bounding box of the element to which the clipping path is applied as the boundaries for the coordinate system of the clipping path. What this means in practice is your values will need to be between 0.0 and 1.0 instead of pixels or some other absolute measurement.
Here’s an example where both the right half and bottom half of a circle have been clipped and only the top left quadrant is visible. The blue outline represents the clipping path.
<svg width="660" height="220" style="outline: 1px solid red">
<clipPath id="clip-3" clipPathUnits="userSpaceOnUse">
<rect x="10" y="10" width="100" height="100" />
<circle cx="110" cy="110" r="100" fill="#9c6" clip-path="url(#clip-3)" />
The clipPath is a rectangle with equal width and height of 100px. I’ve also added clipPathUnits=“userSpaceOnUse” to make it clear, which user coordinate system is in place. Because the system is userSpaceOnUse, the values in the clipping path represent pixels. So x=“10” means x=“10px” and similar for y, width, and height.
Here’s the result, which is probably what you expected.
Here’s the example again. The only difference is I’ve changed the clipPathUnits to objectBoundingBox and because of that change, I’ve also changed the units used to define the clipping path to 0, 0, 0.5, and 0.5.
<svg width="660" height="220" style="outline: 1px solid red">
<clipPath id="quarter-circle-2" clipPathUnits="objectBoundingBox">
<rect x="0" y="0" width="0.5" height="0.5" />
<circle cx="110" cy="110" r="100" fill="#9c6" clip-path="url(#quarter-circle-2)" />
I’ve redrawn the blue rectangle to reflect the complete size of the objectBoundingBox, which is now the boundaries of the circle element. Hopefully this illustrates why I chose values of 0, 0, 0.5, and 0.5 to define the width and height values inside the clipPath.
Values of 0 for x and y represent the left and top edges of the circle’s bounding box and 0.5 for the width and height represent half the distance to the right and bottom edges of that same box.
The clip-rule Property
Last year when I covered fills and strokes, I mentioned the fill-rule property, which is used to determine what’s considered inside the region to be filled and what’s considered outside the same region.
There’s a similar clip-rule which can take the same values as the fill-rule, nonzero or evenodd, to determine what’s inside the clipping area and what’s outside. The value of the clip-rule property will be either nonzero or evenodd.
As was the case with fill-rule, I’m not quite sure I understand how either algorithm works, so I’ll again present the definitions of each as taken from the spec, along with an example of each.
nonzero—This rule determines the “insideness” of a point on the canvas by drawing a ray from that point to infinity in any direction and then examining the places where a segment of the shape crosses the ray. Starting with a count of zero, add one each time a path segment crosses the ray from left to right and subtract one each time a path segment crosses the ray from right to left. After counting the crossings, if the result is zero then the point is outside the path. Otherwise, it is inside.
evenodd—This rule determines the “insideness” of a point on the canvas by drawing a ray from that point to infinity in any direction and counting the number of path segments from the given shape that the ray crosses. If this number is odd, the point is inside; if even, the point is outside.
I find the easiest way to know which value to use is to try them both and see what happens. Most of the time the default, nonzero will likely be what you want.
I realize most of what’s in this post is digging into the weeds a bit, but I’d rather give you too much information than not enough. Hopefully it hasn’t been too much and combined with last week’s post you now have a general idea of how to work with clipping paths.
However, I haven’t given you much variation in the examples so far. That was on purpose so we could focus on how this all works. Now that we’ve worked through the basic information I want to present some varied examples and that’s what I’ll doc the next couple of weeks.
Download a free sample from my book, Design Fundamentals.
I know I’m a bit late, but I hope this scratches that itch.
Below is a 7-minute, 9-second video explaining this algorithm as plainly and clearly as I’ve been able to find.
https://www.youtube.com/watch?v=hvS4JVrUtvY [7m, 9s]
Has a bit of an accent, but perfectly understandable maybe save for a few words you might struggle to interpret here and there.
Also might need to re-watch certain parts to catch up conceptually. I got stuck not realizing where his ‘left’ and ‘right’ were. Just imagine we are looking ‘down at the board’ (eagle eye) and you are shrunk down and walking the ‘scan line’ that he’s drawing (we’d see the top of your imaginary head). The lines on the board are like cars crossing in front of you, left to right (albeit at slight angles and their trajectories are indicated by the arrows he’s drawn on them).
Sorry if none of that made sense, but let me know, and I’d be happy to explain further!
It was kind of mind blowing, I hope you enjoy!