Как залить SVG фоновой картинкой, без искажения фонового изображения?

Здравствуйте! Есть следующая конструкция:

div {
  background: #f5f5f5;
  margin: 20px;
  padding: 20px;
  text-align: center;
}
<div>

  <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="400px" height="55px" version="1.1" viewBox="0 0 47.22 6.49" xmlns:xlink="http://www.w3.org/1999/xlink" <defs>

<pattern id="img1" patternUnits="userSpaceOnUse" width="100%" height="100%"> <image xlink:href="https://i.ibb.co/XFK2qJK/pattern.jpg" width="500px" height="500px" />
</pattern>

</defs>

<path class="fil0" d="M44.45 0c0.27,1.28 1.41,2.24 2.77,2.24l0 1.42c-1.56,0 -2.83,1.27 -2.83,2.83l-41.56 0c0,-1.56 -1.27,-2.83 -2.83,-2.83l0 -1.42c1.35,0 2.5,-0.96 2.77,-2.24 1.73,2.13 10.4,3.74 20.84,3.74 10.44,0 19.11,-1.61 20.84,-3.74z" fill="url(#img1)"/>

</svg>
</div>
<div>
  <img src="https://i.ibb.co/XFK2qJK/pattern.jpg">
</div>

Необходимо залить SVG картинкой, при этом чтобы картинка которая используется в качестве фонового изображения, не была растянута. т.е. если фоновое изображение 500x500px, то оно должно таким и оставаться (500x500).

Что я делаю неправильно? Почему у меня в примере реальный размер фонового изображения 500x500px но когда я добавляю его в SVG оно растягивается примерно до 4000x4000px. Помогите исправить ошибку.

Ниже в примере добавлено для наглядности просто изображение, так как оно должно быть, а в SVG сразу видно что оно растянуто

Благодарю за ответ!


Ответы (1 шт):

Автор решения: Leonid

SVG имеет viewBox="0 0 47.22 6.49", то есть от изображения 500X500 нам видно только 47.22X6.49. ViewBox в аккурат захватывает ваш путь по габаритам, а атрибут width растягивает это "окно" до 400px. Одновременно растягивается и кусочек изображения, естественно.

Если не искать легких путей (давно хотел попробовать), то можно распарсить путь и отмасштабировать его на уровне строки d="M...z". Нужный масштаб получаю делением реальной ширины SVG на ширину viewBox:

scale = svg.getBoundingClientRect().width/svg.getAttribute('viewBox').split(' ')[2];

Так как сам путь уже будет растянут на ширину SVG, то атрибут viewBox в конце удаляем. Ведь в паре с width он работает только для правильного размещения пути. Подстраиваться под ширину контейнера SVG не будет.

Хотя viewBox можно и оставить, изменяя и его размеры и начальные точки:

svg.setAttribute('viewBox', svg.getAttribute('viewBox').split(' ').map(i => i * scale).join(' '));

let svg = document.querySelector('svg');
let pathEl = svg.querySelector('path');

let path = pathEl.getAttribute('d');
let pathWidth = svg.getAttribute('viewBox').split(' ')[2];
let svgWidth = svg.getBoundingClientRect().width;
let scale = svgWidth/pathWidth;

let path1 = path.replace(/(?<=[^\d.-]*)([\d.-]+)(?=[^\d.-]*)/g, m => m*scale);

svg.removeAttribute('viewBox');
pathEl.setAttribute('d', path1);
<svg viewBox="0 0 47.22 6.49" width="400px"> 
    <defs>
        <pattern id="_img1" patternUnits="userSpaceOnUse" width="100%" height="100%"> <image xlink:href="https://i.ibb.co/XFK2qJK/pattern.jpg" width="500px" height="500px" />
        </pattern>
    </defs>

    <path class="fil0" d="M44.45 0c0.27,1.28 1.41,2.24 2.77,2.24l0 1.42c-1.56,0 -2.83,1.27 -2.83,2.83l-41.56 0c0,-1.56 -1.27,-2.83 -2.83,-2.83l0 -1.42c1.35,0 2.5,-0.96 2.77,-2.24 1.73,2.13 10.4,3.74 20.84,3.74 10.44,0 19.11,-1.61 20.84,-3.74z" fill="url(#_img1)"/>

</svg>

Или гораздо проще установить 1/масштаб самому изображению. Чтобы оно уменьшилось с 500*500 до соответствующего viewBox размера.

let svg = document.querySelector('svg');

let viewWidth = svg.getAttribute('viewBox').split(' ')[2];
let svgWidth = svg.getBoundingClientRect().width;
let scale = svgWidth/viewWidth;

svg.querySelector('pattern > image').setAttribute('transform', `scale(${1/scale})`);
<svg viewBox="0 0 47.22 6.49" width="400px"> 
    <defs>
        <pattern id="_img1" patternUnits="userSpaceOnUse" width="100%" height="100%"> <image xlink:href="https://i.ibb.co/XFK2qJK/pattern.jpg" width="500px" height="500px" />
        </pattern>
    </defs>

    <path class="fil0" d="M44.45 0c0.27,1.28 1.41,2.24 2.77,2.24l0 1.42c-1.56,0 -2.83,1.27 -2.83,2.83l-41.56 0c0,-1.56 -1.27,-2.83 -2.83,-2.83l0 -1.42c1.35,0 2.5,-0.96 2.77,-2.24 1.73,2.13 10.4,3.74 20.84,3.74 10.44,0 19.11,-1.61 20.84,-3.74z" fill="url(#_img1)"/>

</svg>

→ Ссылка