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.
Creating the SVG
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>
Masking the Image File
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:
Converting SVG to CSS Clip Path
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:
See the Pen Cutting out images by Mikael Ainalem (@ainalem) on CodePen.
Tricks Using CSS Clip Path
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.
See the Pen Cutting out images #2 by Mikael Ainalem (@ainalem) on CodePen.
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:
See the Pen Cutting out images #3 by Mikael Ainalem (@ainalem) on CodePen.
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.
See the Pen Cutting out images #4 by Mikael Ainalem (@ainalem) on CodePen.
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:
See the Pen clip-path highlight by Mikael Ainalem (@ainalem) on CodePen.
Fading and Blurring Effects
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.
See the Pen Cutting out images #9 by Mikael Ainalem (@ainalem) on CodePen.
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:
See the Pen Cutting out images #10 by Mikael Ainalem (@ainalem) on CodePen.
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.
See the Pen Cutting out images #5 by Mikael Ainalem (@ainalem) on CodePen.
Of course, we can also trigger the outline on hover like we did in the other examples:
See the Pen Cutting out images #6 by Mikael Ainalem (@ainalem) on CodePen.
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:
See the Pen Cutting out images #8 by Mikael Ainalem (@ainalem) on CodePen.
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:
See the Pen Cutting out images #7 by Mikael Ainalem (@ainalem) on CodePen.
Morphing Clip Paths
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.
See the Pen Cutting out images #11 by Mikael Ainalem (@ainalem) on CodePen.
Double Exposure Effect
Another interesting effect we can create with
clip-path is a double exposure. Here are two images blend in the same mask.
See the Pen Double exposure by Mikael Ainalem (@ainalem) on CodePen.
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|
Wrapping it Up
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
It would seem the best use for this is not covered here, clipping image to work over different, varying or moving backgrounds. All of the above examples (sorry to be a party pooper) would be better served as photoshop style edits and uploaded as actual images. Or cropped in an SVG and be done with it. (ie, no need to complicate things with CSS)
I love clip-path, and have already implemented it into a future-proofed solution for styling bread crumbs. And even though caniuse.com shows limited support in Chrome, it works equally well there. :)
lol, I didn’t notice the interactive aspects to the codepens, so I see the point of your examples a little better now. :P
These examples are superb. Thank you.
what a beauty. this gave me so many ideas. thanks!
Thanks for pointing this out! I like the idea to find solutions in CSS and I actually tried to do so here at first. However, I found no really good solution for this case. Here the cut-out part is triggering an effect on the background element and not on itself. It could perhaps be done with :hover + sibling selector + fiddling with z-index. In the end I went with JS as I think readability is an important aspect of the demos included in the article.
Could be a good idea for some apparel website menu, also like the ‘True Detective’ effect :)
Thank you for the examples!
I usually used clip-path as bandwidth or load saver since that way one does not need to re-upload secondary clipped image. Just use one single image twice, one clipped and another is not. From all the interactive effects shown, only the morph clip-path interests me.
Last time I used something like this I separated elements to layers (with original left intact) in Photoshop, then exported those elements as 24 bit PNGs (size reduced with PNGquant). Then, I positioned those elements above the original image with simple SASS mixin where ratio and position was dynamically calculated in percentage (you get a position of slice in Photoshop, as well as with).
After that, you can pretty much do everything without any clip-path (filters, opacity etc.).
Here is a SASS mixin, replace imageHeight and imageWidth with yours. I assume that such image is 100% width of parent, otherwise you have to replace that too.
Brings us closer again to what we once had in Flash :)