Nathan Arthur
Nathan Arthur

Reputation: 9096

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

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:

enter image description here

Upvotes: 2

Views: 3202

Answers (3)

Nathan Arthur
Nathan Arthur

Reputation: 9096

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

Upvotes: 1

Alexandr_TT
Alexandr_TT

Reputation: 14545

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>

Upvotes: 1

Persijn
Persijn

Reputation: 14990

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>

Upvotes: 2

Related Questions