Reputation: 16948
Creating rounded corners is easy, but we are somewhat limited with the flexibility of border-radius
when it comes to applying a different curve to the edges and a different curve to the corners. I have a shape (not sure of it's name...) that I would like to create, ideally, using just CSS.
Consider the following shape:
I have played around with various values within border-radius
but am fairly confident it needs more than just border-radius
. I also considered applying certain transform
's but none of the available tranformations seem appropriate.
Can anyone suggest a CSS trick that would allow one to create this shape? Or, alternatively, how I could go about creating this shape in HTML5 canvas?
border-radius
values such as 20% / 30%
but have not been able to get the desired shapetransform
values but none of the available transforms seem appropriateNOTE 1: Pseudo-elements are probably not going to work here as a) the end shape will be used as a mask for an image, and b) I need to apply a gradient to the shape (as a border)
NOTE 2: I have added a gradient to the image for illustrative purposes as I will need to apply a border to this shape. However, the actual type of gradient, and colours, will differ from what is illustrated. As long as I can add a gradient, I can apply the necessary styles to ensure the gradient is correct
Upvotes: 0
Views: 1162
Reputation: 17165
SVG supports strokes/borders filled with gradients.
So, instead of using two clipped elements you could clip only the <image>
element and apply the gradient stroke directly to the squircle shape:
svg {
width: 10em;
}
<h3>SVG squircle - image clipped</h3>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
<defs>
<!-- reusable squircle path -->
<path id="squircle" d="M 59 10 h 2 c 39.813 0 49 9.188 49 49 v 2 c 0 39.813 -9.188 49 -49 49 h -2 c -39.813 0 -49 -9.188 -49 -49 v -2 c 0 -39.813 9.188 -49 49 -49"/>
<clipPath id="clip">
<use href="#squircle" />
</clipPath>
<radialGradient id="gradient" cx="0" cy="0" r="2" >
<stop offset="0%" stop-color="yellow" />
<stop offset="100%" stop-color="red" />
</radialGradient>
</defs>
<!-- stroke -->
<image href="https://picsum.photos/id/237/200/200" clip-path="url(#clip)" height="100%" width="100%" preserveAspectRatio="xMidYMid meet"/>
<!-- stroke -->
<use href="#squircle" fill="none" stroke="url(#gradient)" stroke-width="10" />
</svg>
Instead of clipping, you could also fill your squircle shape with an image <pattern>
.
See also "Fill SVG path element with a background-image"
<h3>SVG squircle - squircle filled with image</h3>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
<defs>
<pattern id="imgFill" patternUnits="userSpaceOnUse" width="100%" height="100%">
<image href="https://picsum.photos/id/237/200/200" x="0" y="0" width="100%" height="100%" />
</pattern>
<radialGradient id="gradient2" cx="0" cy="0" r="2" >
<stop offset="0%" stop-color="yellow" />
<stop offset="100%" stop-color="red" />
</radialGradient>
</defs>
<path id="squircle2" d="M 59 10 h 2 c 39.813 0 49 9.188 49 49 v 2 c 0 39.813 -9.188 49 -49 49 h -2 c -39.813 0 -49 -9.188 -49 -49 v -2 c 0 -39.813 9.188 -49 49 -49" fill="url(#imgFill)" stroke="url(#gradient2)" stroke-width="10"/>
</svg>
We need to wrap the clipped image in an element that's also clipped with the same squircle.
The stroke-width is set by a padding value .
The gradient fill is defined by the parent's background color.
*{
box-sizing: border-box;
}
.clipped {
aspect-ratio: 1/1;
width: 100%;
object-fit: cover;
object-position: 50%;
clip-path: url(#clipPathSquircle);
}
.squircle-wrp{
width:10em;
height:10em;
aspect-ratio:1;
background: radial-gradient(
farthest-corner at 0px 0px,
yellow 0%, red 100%);
padding:15px;
}
<h3>CSS squircle - wrap and image clipped</h3>
<div class="squircle-wrp clipped">
<img src="https://picsum.photos/id/237/200/200" class="img-square clipped">
</div>
<!-- hidden svg clip asset -->
<svg style="width:0; height:0; position:absolute">
<clipPath id="clipPathSquircle" clipPathUnits="objectBoundingBox"><path d="M 0.492 0.083 h 0.017 c 0.332 0 0.408 0.077 0.408 0.408 v 0.017 c 0 0.332 -0.077 0.408 -0.408 0.408 h -0.017 c -0.332 0 -0.408 -0.077 -0.408 -0.408 v -0.017 c 0 -0.332 0.077 -0.408 0.408 -0.408" /></clipPath>
</svg>
See also "How to create a Squircle with a border?"
Upvotes: 1
Reputation: 151
I believe the shape you're using may be called a "squircle." Regardless, if you can create it as a vector, then you can create an avatar mask using an SVG.
For example, you could make a squircle shape in a vector editing program and use it as a clip path.
<img class="clip-svg" src="https://picsum.photos/450" alt="Lorem Picsum">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 450 450"><title>squircle</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1">
<clipPath id="squircle">
<path d="M225,449.5c-38.23,0-74.86-3.95-105.92-11.43-33.37-8-58.59-19.87-72.94-34.21S20,364.29,11.93,330.92C4.45,299.86.5,263.23.5,225s4-74.86,11.43-105.92C20,85.71,31.8,60.49,46.14,46.14S85.71,20,119.08,11.93C150.14,4.45,186.77.5,225,.5s74.86,4,105.92,11.43c33.37,8,58.59,19.87,72.94,34.21S430,85.71,438.07,119.08c7.48,31.06,11.43,67.69,11.43,105.92s-3.95,74.86-11.43,105.92c-8,33.37-19.87,58.59-34.21,72.94S364.29,430,330.92,438.07C299.86,445.55,263.23,449.5,225,449.5Z"/><path d="M225,1c38.19,0,74.78,4,105.8,11.42,33.29,8,58.42,19.8,72.7,34.08s26.07,39.41,34.08,72.7c7.47,31,11.42,67.61,11.42,105.8s-3.95,74.78-11.42,105.8c-8,33.29-19.8,58.42-34.08,72.7s-39.41,26.07-72.7,34.08C299.78,445.05,263.19,449,225,449s-74.78-3.95-105.8-11.42c-33.29-8-58.42-19.8-72.7-34.08s-26.07-39.41-34.08-72.7C5,299.78,1,263.19,1,225s4-74.78,11.42-105.8c8-33.29,19.8-58.42,34.08-72.7s39.41-26.07,72.7-34.08C150.22,5,186.81,1,225,1m0-1C150.66,0,76.31,15.26,45.79,45.79c-61,61.05-61,297.37,0,358.42C76.31,434.74,150.66,450,225,450s148.69-15.26,179.21-45.79c61.05-61,61.05-297.37,0-358.42C373.69,15.26,299.34,0,225,0Z"/>
</clipPath></g></g></svg>
.clip-svg {
clip-path: url(#squircle);
}
And here it is as a codepen
To add a gradient border, you can use a second path in the squircle. A simple border on the image doesn't work, as it goes around the edges of the rectangular image and gets cut off. Here is a version with a background squircle with a gradient border and a masked image inside it.
<svg width="516" height="516" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 516 516">
<defs>
<style>
.background-squircle{fill:url(#linear-gradient);}
.inner-image{clip-path:url(#clip-path);}
</style>
<clipPath id="clip-path">
<path d="M258,482.5c-38.23,0-74.86-3.95-105.92-11.43-33.37-8-58.59-19.87-72.94-34.21S53,397.29,44.93,363.92C37.45,332.86,33.5,296.23,33.5,258s4-74.86,11.43-105.92c8-33.37,19.87-58.59,34.21-72.94S118.71,53,152.08,44.93C183.14,37.45,219.77,33.5,258,33.5s74.86,4,105.92,11.43c33.37,8,58.59,19.87,72.94,34.21s26.17,39.57,34.21,72.94c7.48,31.06,11.43,67.69,11.43,105.92s-3.95,74.86-11.43,105.92c-8,33.37-19.87,58.59-34.21,72.94S397.29,463,363.92,471.07C332.86,478.55,296.23,482.5,258,482.5Z"/>
</clipPath>
<linearGradient id="linear-gradient" y1="262" x2="524" y2="262" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#fff"/><stop offset="1"/>
</linearGradient>
</defs>
<title>SquircleWithImage</title>
<g id="Avatar_1" data-name="avatar">
<path class="background-squircle" d="M258,515.5c-43.84,0-85.85-4.53-121.47-13.11-38.28-9.22-67.21-22.79-83.67-39.25s-30-45.39-39.25-83.67C5,343.85.5,301.84.5,258S5,172.15,13.61,136.53C22.83,98.25,36.4,69.32,52.86,52.86s45.39-30,83.67-39.25C172.15,5,214.16.5,258,.5S343.85,5,379.47,13.61c38.28,9.22,67.21,22.79,83.67,39.25s30,45.39,39.25,83.67C511,172.15,515.5,214.16,515.5,258S511,343.85,502.39,379.47c-9.22,38.28-22.79,67.21-39.25,83.67s-45.39,30-83.67,39.25C343.85,511,301.84,515.5,258,515.5Z"/>
<g class="inner-image">
<image id="Image" data-name="Layer 0" width="516" height="516" xlink:href="https://picsum.photos/516"/>
</g>
</g>
</svg>
Upvotes: 3