Динамическое изменение размеров отдельных фигур без масштабирования всего SVG

У меня есть стрелка как SVG, длина которой я хочу изменить в ответ на ширину обертки DIV (например).

все мои предыдущие попытки привели к такому поведению (масштабирование всего SVG):

Scaling whole SVG

вместо этого, я хочу добиться такого поведения:

Scaling shapes only

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

SVG как код:

<svg width="35px" viewBox="0 0 35 985" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <g id="Group" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(0.000000, 0.000000)">
    <rect id="Rectangle-2" fill="#5FDCC7" x="12" y="19.7987928" width="11" height="100%"></rect>
    <polygon id="Triangle" fill="#5FDBC6" points="17.5 0 35 20.7887324 0 20.7887324"></polygon>
    <rect id="Rectangle-3" fill="#5FDBC6" x="0" y="973.110664" width="35" height="11"></rect>
  </g>
</svg>

5 ответов


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

ближайшее, что вы можете получить, - это ограничить масштабирование осью X. Однако наконечник стрелы все равно будет растягиваться.

<svg width="35px" height="150px" viewBox="0 0 35 985" preserveAspectRatio="none">

        <g id="Group" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(0.000000, 0.000000)">
            <rect id="Rectangle-2" fill="#5FDCC7" x="12" y="19.7987928" width="11" height="100%"></rect>
            <polygon id="Triangle" fill="#5FDBC6" points="17.5 0 35 20.7887324 0 20.7887324"></polygon>
            <rect id="Rectangle-3" fill="#5FDBC6" x="0" y="973.110664" width="35" height="11"></rect>
        </g>
</svg>


<svg width="35px" height="300px" viewBox="0 0 35 985" preserveAspectRatio="none">

        <g id="Group" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(0.000000, 0.000000)">
            <rect id="Rectangle-2" fill="#5FDCC7" x="12" y="19.7987928" width="11" height="100%"></rect>
            <polygon id="Triangle" fill="#5FDBC6" points="17.5 0 35 20.7887324 0 20.7887324"></polygon>
            <rect id="Rectangle-3" fill="#5FDBC6" x="0" y="973.110664" width="35" height="11"></rect>
        </g>
</svg>

Если вы хотите общее решение, то вы должны контролировать размер события и обновление SVG динамически.

ограниченное хитрое умное решение...

  1. SVG простирается только в одном направлении.
  2. части "non-шкалирования" на любом или обоих концах простирания
  3. конечные части твердые и не имеют никаких отверстий.
  4. ты в порядке с фоном SVG, являющимся сплошным цветом, а не прозрачным.

вот демонстрация вашей формы, реализованной с помощью этой техники. Если вы измените размер окна по горизонтали, вы увидите, что оно растягивается соответствующим образом.:

<svg width="100%" height="35px">

  <svg viewBox="0 0 1 1" preserveAspectRatio="none">
    <rect x="0" y="0" width="1" height="1" fill="white"/>
    <rect x="0" y="0.4" width="1" height="0.2" fill="#5FDBC6"/>
  </svg>
    
  <svg viewBox="0 0 0.2 1" preserveAspectRatio="xMinYMid">
    <rect x="0" y="0" width="0.2" height="1" fill="#5FDBC6"/>
  </svg>

  <svg viewBox="0 0 0.8 1" preserveAspectRatio="xMaxYMid">
    <rect x="0" y="0" width="0.8" height="1" fill="white"/>
    <polygon points="0,0 0.8,0.5 0,1" fill="#5FDBC6"/>
  </svg>

</svg>

Да, вы можете использовать проценты в SVG.

для основных форм его довольно просто. Вы можете написать свою основную прямую кишку как

<rect x="0" y="5" width="100%" height="10"/>

ваш второй прямой кишки еще проще, так как он сидит на 0,0 все время

<rect x="0" y="0" width="10" height="20"/>

но со стрелкой есть проблема в pathes и polygon вы не можете использовать проценты. Чтобы обойти эту проблему, существует двухэтапное решение.

сначала поместите путь в элемент символа:

<symbol id="arrow" viewBox="0 0 20 20" width="20" height="20">
  <path d="M0,0L20 10L0 20z" />
</symbol>
вы можете расположите этот символ так, как вы бы расположили прямую кишку... простой...
<use xlink:href="#arrow" x="100%" y="0" width="20" height="20"/>

но теперь ваша стрелка начинает 100% и полностью находится вне вашего окна просмотра. вы можете просто установить overflow: visible на вашем svg и сделать с ним, но это не то, что мы хотим... мы хотим, чтобы стрелка заканчивалась на 100%. Но это также легко, мы знаем, что стрелка имеет ширину 20px. Поэтому просто переведите использование обратно 20px:

<use xlink:href="#arrow" x="100%" y="0" width="20" height="20" transform="translate(-20 0)"/>

используя этот подход, вы можете расположить любую форму на любой базовой позиции на проценты...

чтобы деформировать все это, ваш полный svg теперь выглядит так:

<svg id="svg" height="20px" xmlns:xlink="http://www.w3.org/1999/xlink">
  <symbol id="arrow" viewBox="0 0 20 20" width="20" height="20">
    <path d="M0,0L20 10L0 20z" />
  </symbol>
  <g id="Group" fill="#5FDCC7">
    <rect x="0" y="5" width="100%" height="10" transform="translate(-20 0)" />
    <rect x="0" y="0" width="10" height="20" />
    <use xlink:href="#arrow" width="20" height="20" x="100%" y="0" transform="translate(-20 0)" />
  </g>
</svg>

и вот фрагмент, использующий этот svg с 3 различными ширинами:

svg:nth-of-type(1) {
  width: 100px
}
svg:nth-of-type(2) {
  width: 200px
}
svg:nth-of-type(3) {
  width: 300px
}
<svg height="20px" xmlns:xlink="http://www.w3.org/1999/xlink">
  <symbol id="arrow" viewBox="0 0 20 20" width="20" height="20">
    <path d="M0,0L20 10L0 20z" />
  </symbol>
  <g id="Group" fill="#5FDCC7">
    <rect x="0" y="5" width="100%" height="10" transform="translate(-20 0)" />
    <rect x="0" y="0" width="10" height="20" />
    <use xlink:href="#arrow" width="20" height="20" x="100%" y="0" transform="translate(-20 0)" />
  </g>
</svg>
<br/>
<svg height="20px" xmlns:xlink="http://www.w3.org/1999/xlink">
  <symbol id="arrow" viewBox="0 0 20 20" width="20" height="20">
    <path d="M0,0L20 10L0 20z" />
  </symbol>
  <g id="Group" fill="#5FDCC7">
    <rect x="0" y="5" width="100%" height="10" transform="translate(-20 0)" />
    <rect x="0" y="0" width="10" height="20" />
    <use xlink:href="#arrow" width="20" height="20" x="100%" y="0" transform="translate(-20 0)" />
  </g>
</svg>
<br/>
<svg height="20px" xmlns:xlink="http://www.w3.org/1999/xlink">
  <symbol id="arrow" viewBox="0 0 20 20" width="20" height="20">
    <path d="M0,0L20 10L0 20z" />
  </symbol>
  <g id="Group" fill="#5FDCC7">
    <rect x="0" y="5" width="100%" height="10" transform="translate(-20 0)" />
    <rect x="0" y="0" width="10" height="20" />
    <use xlink:href="#arrow" width="20" height="20" x="100%" y="0" transform="translate(-20 0)" />
  </g>
</svg>

для этого particlular usecase, я думаю, что я бы пошел на подход CSS, он может (в зависимости от вашей разметки) добавить html-элемент, если вы не можете использовать псевдо-элементы, но это не потребует JS или ручных входных значений для элементов ширины штриха или преобразования.

вот способ сделать эту стрелку с помощью тега абзаца:

p {
  position: relative;
  max-width: 300px;
  border-bottom: 4px solid #5FDBC6;
  padding-bottom:6px;
}
p:nth-child(2) {max-width: 400px;}
p:nth-child(3) {max-width: 200px;}
p::before, p::after{
  content:'';
  position:absolute;
  bottom:-8px;
}
p::before {
  height: 12px;
  left:0;
  border-left: 4px solid #5FDBC6;
}
p::after{
  right:-4px;
  border-style:solid;
  border-width:6px 0 6px 8px;
  border-color:transparent transparent transparent #5FDBC6;
}
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. a dapibus, tincidunt velit eget, rutrum urna.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras in purus risus. Ut id est suscipit, tincidunt sem a, fermentum magna. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut rutrum ac ligula</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras in purus risus. Ut id est suscipit, tincidunt sem a, fermentum magna.</p>

С помощью символа можно сделать фигуры, которые вы хотите, и просто включить их в. Затем, чтобы расширить среднюю часть, просто сделайте новый путь и сделайте линию длиннее. Они могут быть расположены везде, где вам нравится, используя transform свойства

<svg xmlns="http://www.w3.org/2000/svg">

  <symbol id="base">
    <g xmlns="http://www.w3.org/2000/svg">
      <path fill="#5FDBC6" d="M0,0 L0,20 L4,20 L4,0z"></path>
    </g>
  </symbol>
  <symbol id="triangle" >
    <g>
      <path fill="#5FDBC6" d="M0,0 L15,10 L0,20z"></path>
    </g>
  </symbol>

  <path stroke-width="4" stroke="#5FDBC6" d="M0,10 L100,10"></path>
  <g class="first" transform="translate(0,00)">
    <use xlink:href="#base" />
  </g>
  <g class="first" transform="translate(90,00)">
    <use xlink:href="#triangle" />
  </g>
  
  <path stroke-width="4" stroke="#5FDBC6" d="M0,50 L200,50"></path>
  <g class="first" transform="translate(0,40)">
    <use xlink:href="#base" />
  </g>
  <g class="first" transform="translate(200,40)">
    <use xlink:href="#triangle" />
  </g>


</svg>

**редактировать**

это поведение вызвано вашим viewBox атрибут on <svg>. Соотношение сторон SVG сохраняется по умолчанию. Вы хотите, чтобы это поведение на #Triangle и #Rectangle-3, а не #Rectangle-2 (требуется height для увеличения, но width остаются теми же).

я думаю, вы захотите добавить preserveAspectRatio=none на <svg> встроенный CSS, а затем сделайте некоторые настройки (так как это испортит соотношение сторон вашего #Triangle и #Rectangle-3 - или вместо этого вы можете попробовать изменить height of #triangle-2 на ту же высоту, что и ваша обертка div (в моей скрипке JS я сделал это таким образом, установив #Triangle-2s высота до 25vh). Играть с высотой окна - #Rectangle-2's высота удлиняется, но не меняет ширину, хотя я еще не получил ваш #Rectangle-3 динамически придерживаться нижней части #Rectangle-2 (#Rectangle-3 застрял в нижней части окна): https://jsfiddle.net/KyleVassella/dnagft6q/6/

я подозреваю, что вы знаете, как это исправить, но в любом случае я буду продолжать работать над этим, и в то же время вы можете прочитать здесь, где есть гораздо больше информации по этому вопросу:

как я могу сделать шкалу svg с родительским контейнером?