I want to fill a viewport with an svg, and add a fill to a path in this svg.
When I set preserveAspectRatio="xMinYMin slice"
to the image pattern, it will preserve it's aspect ratio nicely, but the path will not scale.
If I then add preserveAspectRatio="none"
to the svg element, the whole element will fit to the viewport, but my image will not scale properly.
What am I overlooking?
<svg height="100%" width="100%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1400 750" preserveAspectRatio="none">
<defs>
<pattern id='home' width="1" height="1" preserveAspectRatio="xMinYMin slice">
<image xlink:href='https://i.imgur.com/4AiXzf8.jpg' width="100%" height="100%" preserveAspectRatio="xMinYMin slice"></image>
</pattern>
</defs>
<path id="bg" class="cls-1" d="M0,0V622.58S250,711,683,711s683-88.42,683-88.42V0Z"style="fill: url(#home)"></path>
</svg>
If you set the whole SVG to preserveAspectRatio="none"
, then everything inside the SVG will stretch and there is no way to counteract that. So you have to do it a different way.
What we have to do is remove the viewBox
and the preserveAspectRatio
and just set the width
of the SVG to 100%
and the height
to the height of our <path>
(711px
). That way the SVG viewport fills the width of the page and keeps its height at the size we want.
The next step is to move the <image>
out of the <pattern>
, make its width and height 100% x 100%
and set preserveAspectRatio="xMinYMin slice"
. So it now scales to fill our wide SVG, and keeps its aspect ratio.
The last step is to apply a <clipPath>
to the image to give it the shape we want. To get the clip path to automatically fit itself to the <image>
, we need to use clipPathUnits="objectBoundingBox"
.
The thing with objetBoundingBox units is that (0,0) represents the top left of the element it is applied to, and (1,1) corresponds to the bottom-right of the element it is applied to. But our path is much bigger than that. It has a width and height of 1366x711.
To fix that, we need to scale the path so that it is only 1x1 in size, instead of 1366x711. So we apply a transform
and scale it by 1/1366
in X, and 1/711
in Y.
transform="transform="scale(0.000732, 0.001406)"
The final result is this:
body {
margin: 0;
padding: 0;
}
<svg width="100%" height="711px" xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="clip" clipPathUnits="objectBoundingBox">
<path d="M0,0V622.58S250,711,683,711s683-88.42,683-88.42V0Z"
transform="scale(0.000732, 0.001406)"></path>
</clipPath>
</defs>
<image xlink:href='https://i.imgur.com/4AiXzf8.jpg' width="100%" height="100%" preserveAspectRatio="xMinYMin slice"
clip-path="url(#clip)"></image>
</svg>