Do you remember being a kid, cutting out pictures from magazines? Did you glue them onto paper to create your own collages? This post is about cutting out images on the web using the CSS property
clip-path. We will discuss how to do the cutting and how we can use these cut-out parts to create some interesting effects, combining these cut-out parts with the original image.
I’ll use the following photo as an example. The flower stands out from the rest of the photo. It is a natural focal point to cut out and create our effects around.
First off, we are going to create a new SVG file and import our example image into it. You will need image editing software with vector capability to make the cut. I’m using Inkscape, a free open source editor, but you can apply the same method covered in this post using other applications such as Adobe Illustrator or even an online editor, like Vectr.
Let’s start out by creating a new
100px square SVG document in the image editor. It’s important to use a
100px square because clipping paths come in length-percent. Choosing a 0-100 scale will allow for seamless conversion from pixel to percent.
One thing that I find very valuable before proceeding is to examine the SVG code output from the editor. Getting that output depends on the application. For example, there are two methods in Illustrator alone. Looking at the markup gives us insight into what the editor is doing behind the scenes because not all applications export SVG the same way. Seeing the code will increase your understanding of the markup. It’s a win-win.
The SVG output should give us something like this:
<svg … width="100px" height="100px" viewBox="0 0 100 100" …> ... </svg>
One of the more important parts of the above is the
viewBox attribute because it represents the internal coordinate system of the SVG document. Here’s a thorough explanation of how it works. Above, we can see that the document has the correct proportions where
viewBox all span from 0 to 100.
Next up is importing the image. Here we’ll want to resize the image to
100px square and place it at the origin
(0, 0). Doing so might break the aspect ratio of your image, unless your image is already in a square proportion. Our example image is not. This will not be a problem when applying
clip-path later on.
Looking at the generated code once again, we now should see the
<image> tag inside the SVG document. Note the
preserveAspectRatio is set to
none. This tells us that the image’s original dimensions are being ignored.
<svg … width="100px" height="100px" viewBox=“0 0 100 100”> ... <image … y="0" x="0" xlink:href=".../image-file-name.jpg" preserveAspectRatio="none" height="100" width="100" … /> ... </svg>
Now for the actual image cutting.
The concept of cutting images is called masking. If you are unfamiliar with masking it’s essentially drawing a closed shape with the pen tool around an area of the image. You don’t need to be savvy with a vector editor to do this. It doesn’t require any particular artistic skills and it can be done in a few basic steps.
Masking digital images is very much like cutting images out of real magazines. The path created in the vector editor is the same you’d follow using a pair of scissors. Select the pen tool and start drawing the outline of the part of the image that you want to cut out. In this case, it’s the flower focal point we pointed out earlier. Create as many points you like along the way to shape the mask. Be sure to close the path as the last step.
It is important to only use cusp nodes when doing the cutting because
clip-path does not support complex shapes, like Bezier curves at the time of this writing. It supports only simple shapes such as polygon, circle and ellipse.
If we view the SVG code now, the output will include a path with all the coordinates of the shape you drew. Here is an abbreviated example of my path’s output:
... <path d="m 52.843605,79.860084 -0.69448,1.767767 -0.883884,0 -1.26269,-1.578364 -0.757615,0.06314 -1.388959,-2.714785 -0.12627,-2.967323 -1.704632,2.525381 -1.136422,-0.126269 -0.505076,-2.841054 -1.515229,1.325825 -1.325825,-0.126269 -0.252538,-1.578363 -0.947018,-0.126269 -0.252538,-0.315673 -0.947018,0.126269 -0.69448,-0.757614 0.126269,-1.641498 -0.441942,-0.252538 -0.189403,-2.588516 -0.505077,-0.06314 -1.010152,0.568211 -0.568211,-1.452094 0.441942,-2.399112 -1.325825,-0.126269 -0.378808,-1.262691 0.378808,-2.08344 0.883883,-1.641498 -1.010152,-1.26269 0.505076,-1.957171 -1.452094,-1.010152 -0.378808,-1.010153 1.136422,-2.209709 -2.209709,-0.378807 -0.441941,-1.704632 0.631345,-2.020305 1.704632,-1.38896 -1.578363,-1.452094 0.568211,-2.462247 0.820749,-0.441942 0.126269,-1.515229 0.757614,-1.073287 0.441942,-1.515228 -0.505076,-1.38896 0,-2.272843 0.505076,-1.010153 1.136422,-0.505076 1.325825,0 0.06313,-0.568211 -0.947018,-2.08344 0.378807,-0.631345 0,-0.441942 1.073288,-0.69448 1.073287,0 0.56821,0.315673 -0.189403,-2.525381 0.189403,-0.883884 0.378808,0.757615 0.06313,-0.883884 0.378807,-0.378807 0.189404,-0.378807 0.126269,-2.08344 0.315673,0.06314 0,-0.568211 0.378807,-0.06313 1.199556,0.568211 0.505076,0.69448 0.252538,-2.08344 0.631346,-0.505076 0.631345,-0.568211 0.441942,-0.505076 0.252538,0.505076 0,-0.883883 1.262691,0.315673 0.820749,-1.894036 1.325825,1.136421 1.073287,-1.452094 0.820749,0.189403 1.010152,1.515229 0.505077,0.757615 0.631345,-1.452095 0.820749,-0.56821 0.820749,0.505076 0.378807,0.631345 0.820749,-0.189403 0.820749,0.947018 0,0.252538 0.69448,-0.126269 0.378807,0.631345 0.820749,0 0.568211,1.515229 0.378807,1.325825 0.505076,-0.189404 0.252538,0.441942 0.378808,0.126269 0.441941,2.08344 0,0.568211 0.505077,-0.126269 0,0.883883 0.694479,-0.252538 0.505077,0.505076 0.252538,0.947018 0,0.883884 0.315673,0 0.378807,0.631345 0.441941,0.631345 0.06314,1.515229 -0.378807,1.957171 -0.441942,1.767767 2.904189,-1.136422 0.252538,0.631345 0.126269,2.209709 -0.883884,1.830902 1.38896,0.378807 1.010153,1.199556 -0.378808,1.641498 -0.947018,1.767767 -0.505076,0.378807 0.69448,1.767767 1.010153,1.26269 0.378807,1.38896 -0.378807,1.515229 -0.568211,0.315673 -0.505077,1.010152 -1.452094,0.883884 0.189404,1.325825 0.315672,0.883883 -0.378807,1.38896 -1.388959,1.073287 -0.505077,0.126269 0,0.505077 -0.189403,1.830901 -1.010153,0.631345 0.820749,2.209709 -0.631345,1.452094 -1.641498,-0.189403 0.126269,1.578363 -0.315673,1.641498 -1.073287,0.505076 -0.378807,0.315673 -0.378807,0.883883 -0.252538,1.010153 0.06313,2.714785 -0.631345,0.631345 -1.578364,-0.883883 -0.757614,-1.262691 -0.189404,2.462247 0.189404,2.083439 -0.252538,2.588516 -0.441942,1.894036 -0.631345,0.631346 -0.631345,-0.189404 -0.820749,-0.883883 z" /> ...
Below is a video of me cutting out the flower and the result. The cutting took me about two minutes and the result is pretty decent.
Here is my image with a slight opacity on the mask to show the final shape that was cut out:
Now that we have the mask let’s have a look at how we go from SVG to
clip-path. This means converting the path descriptor, or the
d attribute in the SVG code.
Before we look into how to do the conversion, let’s talk a bit about the reasons for using clipping paths. You might ask why we’re creating a clipping path at all? Why not mask the image in the vector editor and export a pre-cut image? It’s possible and using images is a lot more convenient than working with a huge chunks of CSS code. But there are two principal benefits of going with the clipping path as I see it: interactivity and compression. SVG is essentially code in the DOM that can be manipulated and it is a much smaller file size than a bitmap image of the same shape.
The syntax for CSS clipping paths is somewhat the reverse of what it is in SVG. Pairs are comma-separated and spaces separate coordinates. This is the complete opposite to the SVG descriptor syntax. To further complicate the conversion, some shapes only use absolute coordinates. SVG paths are more flexible as they can use both coordinate systems.
I’ve created a rudimentary Node script that converts SVG paths. It takes paths in relative coordinates and outputs corresponding polygons using CSS
clip-path. It uses regex to parse SVG files. Feel free to fork it and enhance it. One obvious addition would be to add normalization of proportions. Adding normalization would eradicate the need to only use square images when creating masks.
Here is the result of applying the clip-path to the photo of the flower:
Now that we have the cut-out part let’s have a look what we can with it.
One quite neat trick is to stack the cut-out part on top of the original image. Below is a pen illustrating the idea of overlaying the cut-out part on top of the original image. It will give you an idea of the positioning and the two different parts involved. Having these two different elements gives us the possibility to apply separate effects on the foreground and the background respectively.
Highlighting parts on an image is not just visually appealing. It can also have a real impact on the user experience of your site. It isn’t hard to come up with useful scenarios where you might like to highlight parts of an image in a web page. Highlighting tagged people in a photo could be one use-case. Another could be to highlight certain features of a product in a product showcase. A third case could be a photo of a map, in which you would like to highlight places to do some storytelling around. Highlighting or emphasizing parts of the UI can when properly applied be powerful UX pattern. Using clipping paths is one way to achieve highlights in your UI.
Going back to the photo of the flower we can now easily make the flower stand out. One way to achieve this is to tone down the background behind the flower by lowering its opacity:
addEventListener) and attach them to the masked element. Setting these handlers to trigger on events like
mouseout captures the user hovering the flower. We can even toggle classes on the background element to trigger the effect. A CSS opacity transition is essentially what’s needed.
There is nothing that prevents us from reusing the above technique several times in the same image. Here’s an example of highlighting many people in a photo. In this case there are several overlaid cut-outs:
An effect that we’ve been seeing more in the last year is blurred backgrounds. It’s kind of a reverse way to enhance foreground elements. Rather than enhancing the foreground element itself one can create the same effect by blurring background elements. This way of enhancing foreground elements has another pleasant side-effect: the element in your current focal point remains untouched. But, at the same time, it becomes more prominent.
Having a transition using the CSS filter, however, is very costly in terms of performance. This has to do with the shader running on the GPU used to create the blur effect. Animating the CSS blur filter is generally not a good idea. A more performant option is to reuse pre-filtered version of the image and use a cross-fade. In other words, we animate the opacity of a duplicated background image instead of animating the blur. Here is what that looks like:
Another option to reinforce the cut-out element is to use an outline effect. Reusing the mask is an easy way to make it. If we insert the SVG in between the two main elements and add a slight scale (
1.04 in this case), we’ll make it appear as an outline.
Of course, we can also trigger the outline on hover like we did in the other examples:
The edges of the mask are a bit rough as the mask is binary. One option to soften the edges is to add an SVG filter. Here is an example:
What if the part you want to cut out has holes in it? What if it shows parts of the background that you would like to exclude?
For instance, imagine you want to cut out a donut. Then you want the mask to exclude the hole in the middle. How do you then cut out the mask? The
clip-path specification doesn’t allow more than one polygon unless we use SVGs. This means there is no possible way to create more than one shape at a time.
Well, one way to create these holes is to use very thin connectors and draw it as one shape. In other words, we can make a very thin incision from the edge and cut out the hole. This pen illustrates what it looks like when we make these connectors very thin:
To make the highlight effect even more playful we can actually morph the
clip-path itself. Below is an example of creating a dynamic highlight of a butterfly in three shots. The highlight is morphed between the three different cut-out parts on hover.
Another interesting effect we can create with
clip-path is a double exposure. Here are two images blend in the same mask.
clip-path be used in all browsers? Unfortunately, not at the moment! If we look the caniuse table it kinda looks a bit like a traffic light at the time of this writing.
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Mobile / Tablet
|Android Chrome||Android Firefox||Android||iOS Safari|
Some key points I hope you walk away with from this post:
- Using clipping paths is one way to make parts of your images stand out
- Stacking the cut-out part on top of the original image makes it possible to create different kinds of highlight effects in images
- We can leverage the browser’s hit testing on masks to create interactive effects on cut-out parts
clip-pathproperty makes way for UX patterns that highlight and create effects around parts of your images