Search code examples
htmlcssimagesvgaspect-ratio

SVG image not aligning to equivalent of 'object-fit' in 100vh viewport


I'm trying to get an SVG image to be the equivalent of object-fit inside an 100vh full screen container. From what I can gather I need to use the preserveAspectRatio="xMidYMid slice" attribute and set width="100%" height="100%"

I can't seem to get it work though? When I play around with it, there is either a chunk of whitespace showing at the bottom of the container, or when the image does fill the viewport the viewport then becomes a lot bigger than 100vh?

Just to note I can't use the CSS background-image property because it is part of a wider SVG / Javascript animation I'm building.

Codepen: https://codepen.io/emilychews/pen/YJYrEa

Does anyone know what is going wrong here and what the solution would be?

Thanks in advance for any help / ideas.

body {
  margin: 0;
  padding: 0;
  display: flex;
  width: 100%;
  height: 100vh;
  justify-content: center;
  align-items: center;
}

#section-1, .background-clipped {
  width: 100%;
  height: 100%;
}
<section id="section-1">
<div class="background-clipped background-clipped-1">
    <svg id="home-image-1" viewbox="0 0 100 100" preserveAspectRatio="xMidYMid slice">
    <image x="0" y="0" xlink:href="https://i.postimg.cc/9XdQKYF1/dimon-blr-309444-unsplash.jpg" width="100%" height="100%" />
    </svg>
</div>
</section>


Solution

  • There are multiple sizing mechanisms at work; one for the <svg> element, one for the <image> element.

    The one you set for the <svg> element is not really helpfull. The viewBox attribute fits a coordinate system of 100 by 100 user units inside the container. This coordinate system is then the viewport for sizing the <image> with an implicit preserveAspectRatio="xMidYMid meet". If you find that confusing: yes, that is the problem.

    Instead, just size the <svg> element to fill the container. Remove the viewBox, set width and height to 100%. Without a viewBox, preserveAspectRatio also has no effect on the <svg> element.

    Now, only the sizing mechanism for your image (fit the nimage into the 100% width and height of the viewport, as preserveAspectRatio describes), takes effect and works as you want it to.

    General note: a JPG, as used in your <image>, has an inherent aspect ratio. Therefore preserveAspectRatio will always have an effect. A <svg> element has not. Only a viewBox has one. and is what the attribute works on.

    body {
      margin: 0;
      padding: 0;
      display: flex;
      width: 100%;
      height: 100vh;
      justify-content: center;
      align-items: center;
    }
    
    #section-1, .background-clipped {
      width: 100%;
      height: 100%;
    }
    <section id="section-1">
    <div class="background-clipped background-clipped-1">
        <svg id="home-image-1" width="100%" height="100%">
            <image xlink:href="https://i.postimg.cc/9XdQKYF1/dimon-blr-309444-unsplash.jpg"
                   x="0" y="0" width="100%" height="100%" preserveAspectRatio="xMidYMid slice"/>
        </svg>
    </div>
    </section>