Equivalent of "background-size: fill" for SVG <image> element - html

I have a situation where I have a SVG graphic whose width is a percentage of the viewport, and whose height is fixed. Inside the graphic I have an SVG image element which I want to always fill its immediate container without distorting, much as would be achieved by using CSS background-size: fill.
How can I achieve this using my SVG?
Here's a minimal setup of the problem (and a codepen):
<svg width=100% height=300>
<rect width=100% height=300 stroke="blue" stroke-width=30 fill="transparent" />
<image xlink:href="//unsplash.it/500/300" width=100% height=100% />
</svg>
In the above snippet, I need the image to fill the entire container without distorting (in this instance, the container being the root SVG), regardless of viewport width.
I can't switch to regular HTML because the graphic I'm working on has an SVG clipping mask applied to the image element.
I found this question, which seems close to what I'm asking, but I don't think answers my question:
How can I make my embedded svg image stetch to fill a container?
In hopes of avoiding the XY problem, here's the actual SVG graphic I'm working on. And the (fixed-height, variable-width) result I'm trying to achieve:

Svg scaling with image
If you add a viewbox to the svg the svg knows how to scale.
Now adding the same viewBox ratio as the svg makes it so the svg will always have the same scale as the image.
You can always scale the image to its inside the viewBox.
Then it will always scale with the svg.
svg {
border: 2px solid black;
}
<svg width="100%" height="100%" viewBox="0 0 500 300">
<image width="500" height="300" xlink:href="//unsplash.it/500/300"/>
</svg>

To keep the height of the image unchanged, set the fixed value of the viewport height and preserveAspectRatio = "none"
<svg width="100%" height="300" viewBox="0 0 500 300" preserveAspectRatio = "none">
<image width="500" height="300" xlink:href="//unsplash.it/500/300"/>
</svg>
UPD
The proportions of the picture will not change, the height remains fixed when using preserveAspectRatio="xMinYMin slice"
<svg width="100%" height="300" viewBox="0 0 500 300" preserveAspectRatio="xMinYMin slice">
<image width="100%" height="300" xlink:href="//unsplash.it/500/300" />
</svg>
UPD2
I looked your codepen Like the idea. I've finished it with your permission. Modified some parameters
<svg width="100%" height="400" preserveAspectRatio="xMinYMin slice">
<defs>
<mask id="clipping">
<rect width="100%" height="400" fill="white"/>
<ellipse cx="50%" cy="120%" rx="75%" ry="37%"/>
</mask>
<linearGradient id="Gradient1" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stop-color="white"/>
<stop offset="100%" stop-color="blue"/>
</linearGradient>
</defs>
<rect width="100%" height="400" fill="orange" mask="url(#clipping)" transform="translate(-20 12) rotate(-2.5)" />
<image xlink:href="https://unsplash.it/1000/500" width="100%" height="400" mask="url(#clipping)" transform="translate(0 -10)" />
<rect width="100%" height="400" fill="url(#Gradient1)" mask="url(#clipping)" transform="translate(0 -10)" opacity="0.25" />
</svg>

I ended up using two SVGs and an image which used a clipping mask defined in the first SVG to clip it down. I was able to make the SVGs scale by only defining a viewbox, not a width and height. I opted to use a fixed aspect ratio instead of a fixed height, percentage width, as I had originally planned.
body > * {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
img {
clip-path: url(#clipper);
}
<svg viewBox="0 0 1000 500">
<defs>
<symbol id="arch" viewBox="0 0 1000 400">
<path d="M0 0 H 1000 V 400 Q 500 300, 0 400" />
</symbol>
<clipPath id="clipper" clipPathUnits="objectBoundingBox">
<path d="M0 0 H 1 V 1 Q .5 .75, 0 1" />
</clipPath>
<linearGradient id="Gradient1" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stop-color="#87AFBF"/>
<stop offset="100%" stop-color="#002855"/>
</linearGradient>
</defs>
<use href="#arch" width="100%" y="-40" fill="#F9B000" transform="translate(-20 12) rotate(-2.5)" />
</svg>
<img src="//unsplash.it/1000/400" alt="">
<svg viewBox="0 0 1000 500">
<use href="#arch" y="-50" fill="url(#Gradient1)" opacity="0.75" />
</svg>
CodePen

Related

How to preserve a clipped image aspect ratio within an SVG aspect ratio of none

Im trying to make sure the height and width of an SVG divider is responsive but also clip an image into one of the layers to get this effect:
HERE
The issue I'm having right now since the only way to make this responsive both vertically and horizontally with 100vh and 100vw is the image squishing and skewing. Is there a way to preserve the aspect ratio of the image while retaining its responsiveness? Am I missing something basic here?
A link to my current attempt:
https://codepen.io/Tay8472/pen/mdLpOqw
Code snippet below:
<svg id="test_clip" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1069" preserveAspectRatio="none">
<clipPath id="mask_cc_1">
<path d="M0,1025.25c246.56-43.45,560.46-69.79,891-10.97,34.54,6.15,49.46,9.7,83.74,15.91,219.17,39.72,525.11,60.44,945.26,4.92V0H0V1025.25Z"/>
</clipPath>
<image id="test_cc" class="image__svg-image" width="110%" height="110%" clip-path="url(#mask_cc_1)" x="-35%" preserveAspectRatio="xMinYMin" xlink:href="https://www.cupcakebowls.com/wp-content/uploads/2022/09/overlay_cupcake_v1.svg" />
</svg>
<!-- <svg id="test_clip_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 506" >
</svg> -->
<svg id="home_gradient_backdrop" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1920 1069" preserveAspectRatio="none">
<defs>
<style>.home_gradient_overlay{fill:url(#radial-gradient);}</style>
<radialGradient id="radial-gradient" cx="1303.67" cy="727.5" fx="1303.67" fy="727.5" r="1555.2" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#f5428d"/>
<stop offset=".3" stop-color="#da40a1"/>
<stop offset=".95" stop-color="#953dd5"/>
<stop offset="1" stop-color="#903dda"/>
</radialGradient>
</defs>
<path class="home_gradient_overlay" d="M0,1025.25c246.56-43.45,560.46-69.79,891-10.97,34.54,6.15,49.46,9.7,83.74,15.91,219.17,39.72,525.11,60.44,945.26,4.92V0H0V1025.25Z"/>
</svg>
<svg id="home_pink_secondary_backdrop" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1080" preserveAspectRatio="none">
<defs>
<style>.home_pink_backdrop{fill:#ffaed0;}</style>
</defs>
<path class="home_pink_backdrop" d="M0,1080c227-61.03,475.1-86,816-67.91,146.98,7.8,214.97,30.58,381,43.87,299.08,23.93,552.7,2.08,723-20.84V0H0V1080Z"/>
</svg>

A way to produce inner radial shadow hover image with svg

I'm looking for a way to produce an inner radial shadow hover any image with css and svg (or other ?).
Here is an example of what i would like to do (for header and footer parts)
Have you got any suggestions ? I would like a way the most cross browser as possible.
Thank you !
Do you mean like this?
body{background:black;}
svg{height:100vh; display:block; margin:0 auto;}
<svg viewBox="0 0 300 300">
<defs>
<radialGradient id="rg" cx=".5" cy=".5" r=".5">
<stop offset="30%" stop-color="white"></stop>
<stop offset="100%" stop-color="black"></stop>
</radialGradient>
<mask id="mascara">
<circle cx="150" cy="150" r="100" fill="url(#rg)"></circle>
</mask>
</defs>
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/darwin300.jpg" height="240" width="250" style="mask: url(#mascara)"></image>
</svg>
You may need to detect the position of your mouse and use it as the center for the mask.

svg shadow does not appear

I am having a problem with setting up shadow for a svg with mask applied to it. This is the code and jsfiddle: http://jsfiddle.net/3kxnmhfL/
.watch-video-svg {
width: 50px;
height: 50px;
}
<div class="watch-video-svg">
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" height="100%" width="100%" viewBox="0 0 100 100">
<defs>
<filter id="shadow">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
<mask id="cut-off-bottom">
<circle cx="50%" cy="50%" r="50%" fill="white"/>
<polygon points="31,20, 31,77, 80,50" fill="black"/>
<!-- <rect x="0" y="0" width="200" height="200" fill="url(#Gradient)" /> -->
</mask>
</defs>
<circle cx="50%" cy="50%" r="50%" fill="red" mask="url(#cut-off-bottom)" filter="url(#shadow)" />
</svg>
</div>
I'd like the shadow to appear only around svg circle and not containing div.
What could be the reason for shadow not showing up?
SVG filters have a "filter region". The filter region defines the area of pixels that the browser uses to store the result of the filters. The default filter region is the bounds of the element (that the filter is applied to) plus a margin around it to allow for filter elements that have results larger than the original element.
By default, that margin is 10% of the width and height, applied to all four sides. However in your case, a stdDeviation of "4" causes the blur to extend further than that 10% margin allowance. The result is a clipped blur, even though the viewBox has been enlarged enough to cater for the full blur.
To fix, this, you just need to bump up the filter region size. An allowance of 20% seems to work okay:
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
In addition, I've simplified the SVG by getting rid of the unnecessary mask. I also tweaked the viewBox to include the part of the blur that extends to the left.
.watch-video-svg {
width: 500px;
height: 500px;
background: linen;
}
<div class="watch-video-svg">
<svg xmlns="http://www.w3.org/2000/svg" height="100%" width="100%" viewBox="-5 0 120 120">
<defs>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
</defs>
<circle cx="50" cy="50" r="50" fill="red" filter=url(#shadow) />
<polygon points="31,20, 31,77, 80,50" fill="white"/>
</svg>
</div>
You need to adjust your mask. Since your element is already a circle you don't need a circle in the mask, a rect to fill all the width/height is enough.
You need to also adjust the viewBox to leave some space for the shadow:
.watch-video-svg {
width: 50px;
height: 50px;
}
<div class="watch-video-svg">
<svg xmlns="http://www.w3.org/2000/svg" height="100%" width="100%" viewBox="0 0 120 120">
<defs>
<filter id="shadow">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
<mask id="cut-off-bottom">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<polygon points="31,20, 31,77, 80,50" fill="black"/>
</mask>
</defs>
<circle cx="50" cy="50" r="50" fill="red" mask="url(#cut-off-bottom)" filter=url(#shadow) />
</svg>
</div>

SVG image - fixed height - keep ratio - slice

I have a SVG that needs to have a fixed height so its not super big when the width is 2000+ pixels (widescreens...)
The clipping mask should always be visible and the background image should be sliced and not be stretchend, i tried several things but nothing seems to work.
This is what i have now:
https://codepen.io/bucky208/pen/OEqbPp
div {
width: 100%;
}
<div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1381.5" preserveAspectRatio="none" style="display: block; position: absolute; width: 100%;height: 400px;">
<g id="clipgroup">
<defs>
<polygon id="mask" points="0,572.1 0,1381.3 1024,1040.5 1024,337.6 0,0"/>
</defs>
<clipPath id="mask_1_">
<use xlink:href="#mask" style="overflow:visible;"/>
</clipPath>
<g style="clip-path:url(#mask_1_);">
<image style="overflow:visible;" width="331" height="444" id="theimage" xlink:href="https://image.ibb.co/ipbNkJ/56_E0406_E5_C8_EF897.jpg" transform="matrix(3.1119 0 0 3.1111 -3.0528 -2.604167e-04)"></image>
</g>
</g>
</svg>
</div>
Do i need another wrapper around everything? How do i restore the image ratio?
Kind regards and a big thank you for everyone trying to help.
In order to get image fills to fill their container but preserve the original aspect ratio, a filter combined with objectBoundingBox sizing and preserveAspectRatio is your friend. The following code does what I think you want:
svg {
background: red;
}
#svgcont {
position: absolute;
width: 100%;
}
<div id="svgcont">
<svg x="0" y="0" width="100%" height="800px">
<defs>
<filter id="image-fill-nostretch" primitiveUnits="objectBoundingBox">
<feImage x="0" y="0" width="1" height="1" id="theimage" xlink:href="https://image.ibb.co/ipbNkJ/56_E0406_E5_C8_EF897.jpg" preserveAspectRatio="xMidYMid slice"/>
<feComposite operator="in" x1="SourceGraphic"/>
</filter>
<clipPath id="mask_1_" clipPathUnits="objectBoundingBox">
<polygon id="mask" points="0,0.5 0,1 1,0.75 1,0.25 0,0"/>
</clipPath>
</defs>
<g clip-path="url(#mask_1_)">
<rect width="100%"height="100%" x="0%" y="0%" filter="url(#image-fill-nostretch)"/>
</g>
</svg>
</div>

Fill responsive svg with background image with preserving ratio

I try to draw a svg in my HTML Code to have a specific path/object with an background image.
The object should be a little bit responsive (using bootstrap), but filled with the image and the image should preseve its ratio.
<svg width="100%" height="370px" viewBox="0 0 1148.942 598.47" preserveAspectRatio="none" >
<defs>
<pattern id="img1" patternUnits="userSpaceOnUse" width="1153" height="680">
<image xlink:href="images/headerBackground.png" x="0" y="0" width="1153" height="680" />
</pattern>
</defs>
<path fill="url(#img1)" d="M1145.237,3.395H3.379v592c0,0,247.108-160.416,1141-99L1145.237,3.395z"/>
</svg>
You can see it here in the live demo:
https://liveweave.com/N5nib6
https://jsfiddle.net/zyyvd86g/
Maybe anybody can help? I hope the problem is clear enough.
You can use max-width:100% for the svg element and div element wrap on this svg.
You can get the responsive image
div {
width: 80%;
height: auto;
margin: 0 auto;
}
svg {
max-width: 100%;
height: auto;
}
<div>
<svg width="100%" height="370px" viewBox="0 0 1148.942 598.47" preserveAspectRatio="none" >
<defs>
<pattern id="img1" patternUnits="userSpaceOnUse" width="1153" height="680">
<image xlink:href="images/headerBackground.png" x="0" y="0" width="1153" height="680" />
</pattern>
</defs>
<path fill="url(#img1)" d="M1145.237,3.395H3.379v592c0,0,247.108-160.416,1141-99L1145.237,3.395z"/>
</svg>
</div>
IMO opinion, the simple solution to your problem is to use a different preserveAspectRatio on your SVG.
Using preserveAspectRatio="none" is going to stretch your SVG and cause problems.
I'm assuming you want to keep the shape of the "swoop" on the bottom of your path. Correct?
If that is the case, you might prefer to use
preserveAspectRatio="xMidYMax slice"
instead. This scales the SVG up to fill the full width of the SVG viewport, whilst keeping the aspect ratio the same, and keeping the bottom of the SVG viewBox on screen.
<svg width="100%" height="370px" viewBox="0 0 1148.942 598.47" preserveAspectRatio="xMidYMax slice">
<defs>
<pattern id="img1" patternUnits="userSpaceOnUse" width="1153" height="680">
<image xlink:href="http://lorempixel.com/1153/680/" x="0" y="0" width="1153" height="680" />
</pattern>
</defs>
<path fill="url(#img1)" d="M1145.237,3.395H3.379v592c0,0,247.108-160.416,1141-99L1145.237,3.395z"/>
</svg>