动态调整单个形状的大小而不缩放整个 SVG

Posted

技术标签:

【中文标题】动态调整单个形状的大小而不缩放整个 SVG【英文标题】:Resizing single shapes dynamically without scaling the whole SVG 【发布时间】:2017-05-06 15:15:42 【问题描述】:

我有一个箭头作为 SVG,我想根据包装 DIV 的宽度来改变它的长度(例如)。

我之前的所有尝试都导致了这种行为(缩放整个 SVG):

而不是这个,我想实现这种行为:

是否可以通过简单地将百分比值添加到代码中来更改 SVG 内的单个形状?如果没有,我该怎么做?

SVG 作为代码:

<svg  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- fill="none" fill-rule="evenodd" transform="translate(0.000000, 0.000000)">
    <rect id="Rectangle-2" fill="#5FDCC7" x="12" y="19.7987928"  ></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"  ></rect>
  </g>
</svg>

【问题讨论】:

所以你只希望中间线在空间上延伸? 是的,正是...(但动态响应,比如说一个 div 的宽度) 好的,给我 5 分钟 【参考方案1】:

作为一般规则,无法创建部分无法缩放的可缩放 SVG。如果一个 SVG 有一个 viewBox,并且你缩放它,那么所有的内容都会缩放。无法将某些内容标记为不受缩放影响。

你能得到的最接近的是限制缩放到 X 轴。但是箭头仍然会伸展。

<svg   viewBox="0 0 35 985" preserveAspectRatio="none">

        <g id="Group" stroke="none" stroke- fill="none" fill-rule="evenodd" transform="translate(0.000000, 0.000000)">
            <rect id="Rectangle-2" fill="#5FDCC7" x="12" y="19.7987928"  ></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"  ></rect>
        </g>
</svg>


<svg   viewBox="0 0 35 985" preserveAspectRatio="none">

        <g id="Group" stroke="none" stroke- fill="none" fill-rule="evenodd" transform="translate(0.000000, 0.000000)">
            <rect id="Rectangle-2" fill="#5FDCC7" x="12" y="19.7987928"  ></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"  ></rect>
        </g>
</svg>

如果您想要一个通用的解决方案,那么您必须监控调整大小事件并动态更新 SVG。

有限的偷偷摸摸的聪明解决方案......

说了这么多,有一种聪明的技术可以在有限的情况下工作。限制是:

    SVG 仅向一个方向延伸。 “非缩放”部分位于拉伸的任一端或两端 端部是实心的,没有任何孔。 您可以将 SVG 的背景设为纯色,而不是透明。

这是使用此技术实现的形状的演示。如果您水平调整窗口大小,您会看到它适当地拉伸。:

<svg  >

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

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

</svg>

【讨论】:

因为我可以忍受这些限制,这对我来说非常适合!谢谢! 多么狡猾和聪明 嗨,保罗,还有一个更通用的解决方案。使用百分比和变换,您也可以从左侧和底部定位符号,因此您可以在响应式 svg 中的任何位置定位任何形状...请参阅下面的答案... ;-) 太聪明了。这对于使用 SVG 的好处来说已经成熟了,我一直不明白为什么它本质上是不可能的。【参考方案2】:

是的,您可以在 svg 中使用百分比。

对于基本形状,它非常简单。你可以把你的主要矩形写成

<rect x="0" y="5"  />

您的第二个矩形更简单,因为它始终位于 0,0

<rect x="0" y="0"  />

但是箭头在路径和多边形中存在问题,您不能使用百分比。要解决此问题,有一个两步解决方案。

先把路径放在一个符号元素中:

<symbol id="arrow" viewBox="0 0 20 20"  >
  <path d="M0,0L20 10L0 20z" />
</symbol>

现在您可以像定位矩形一样定位此符号...简单...

<use xlink:href="#arrow" x="100%" y="0"  />

但是现在您的箭头开始 100% 并且完全在您的视口之外。你可以只设置溢出:在你的 svg 上可见并完成它,但这不是我们想要的......我们希望箭头以 100% 结束。但这也很简单,我们知道箭头是 20px 宽。所以只需将 use 翻译回 20px:

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

使用这种方法,您可以根据百分比将任何形状定位在任何位置...

为了将它全部扭曲,您的完整 svg 现在看起来像这样:

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

这是一个使用这个 svg 的 sn-p,具有 3 种不同的宽度:

svg:nth-of-type(1) 
  width: 100px

svg:nth-of-type(2) 
  width: 200px

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

【讨论】:

请问我的回答有什么问题?很高兴更正它......【参考方案3】:

对于这个特殊的用例,我想我会选择 CSS 方法,如果你不能使用伪元素但它不需要 JS 或手动输入值,它可能(取决于你的标记)添加一个 html 元素用于笔划宽度或变换元素。

这是一种使用段落标签制作此箭头的方法:

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>

【讨论】:

这也是一个绝对有效的好方法。很高兴看到伪元素如何解决这个问题!【参考方案4】:

通过使用符号,您可以制作您想要的形状并将它们包含在内。然后要扩展中间部分,只需制作一条新路径并使线条更长。可以使用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- 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- 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>

【讨论】:

谢谢!由于这对我来说是一个全新的话题,我从未考虑过这种方法。我在这里有很多要阅读的内容,下一步是修补所有位置和翻译,以实现理想的动态行为。 @to7be 如果它最终对你有用,别忘了接受:) 这并不能真正回答 OP 的问题。 OP 想知道如何在 SVG 的一部分无法缩放的情况下制作响应式 SVG。这基本上是不可能的。 @PaulLeBeau 什么? SVG 无法缩放,但我给出了使用符号的解决方案?这是一个可行且简单的解决方法吗? 您必须通过 javascript 来缩放各个部分,并在整个 SVG 缩放时更新它们的位置和大小。正如 Paul 所说,这里不可能使用非 JavaScript 解决方案。【参考方案5】:

** 已编辑 **

此行为是由&lt;svg&gt; 上的viewBox 属性引起的。默认情况下会保留 SVG 的纵横比。您希望#Triangle#Rectangle-3 上的这种行为,而不是#Rectangle-2 上的这种行为(您希望height 增加,但width 保持不变)。

我认为您需要将 preserveAspectRatio=none 添加到您的 &lt;svg&gt; 内联 CSS 中,然后进行一些调整(因为这会弄乱您的 #Triangle#Rectangle-3 的纵横比 - 或者不是那样,您可以尝试将#triangle-2height 更改为与包装器div 相同的高度(在我的JS 小提琴中,我已经这样做了,将#Triangle-2s 高度设置为25vh)。玩窗口的高度 - #Rectangle-2 的高度会拉长但不会改变宽度,虽然我还没有让你的#Rectangle-3 动态地粘在#Rectangle-2 的底部(#Rectangle-3 卡在窗口底部):https://jsfiddle.net/KyleVassella/dnagft6q/6/

我怀疑你会知道如何解决这个问题,但无论哪种方式,我都会继续努力,同时你可以在这里阅读有关此主题的更多信息:

How can I make an svg scale with its parent container?

【讨论】:

感谢链接!在 SVG 领域,您必须先了解许多基础知识,然后才能获得成功。 preserveAspectRatio 非常有帮助。对我来说,您的解决方案非常朝着正确的方向发展。下一步是自动将#Triangle 对齐到顶部,将#Rectangle-3 对齐到包装DIV 的底部。这个定位的事情对我来说仍然是一个挑战:) thx! 你的建议是错误的。 preserveAspectRatio 不是 &lt;rect&gt; 元素的有效属性。它不会有任何影响。 @Paul LeBeau 你是对的,感谢您指出这一点。我已经编辑了我的答案。 @to7be,告诉我吧!下次我会用链接发表评论,我认为在这种情况下这会更有帮助(也更合适)。我希望有人来解决这个问题。睡一会我会继续努力的。 感谢您的努力。在很多方面,我对 SVG 抱有错误的期望,并且在该领域受到了影响。 Paul LeBeau 和 web-tiki 的方法效果很好。无论如何谢谢。

以上是关于动态调整单个形状的大小而不缩放整个 SVG的主要内容,如果未能解决你的问题,请参考以下文章

Numpy 调整大小/重新缩放图像

调整 CGPath 大小

Qt QGraphicsSvgItem 缩放和调整大小

使用 Javascript 动态添加时,SVG 元素无法正确缩放

动态调整内联 SVG 的大小

如何调整 SVG 剪辑路径的大小?