【问题标题】:Why doesn't an SVG with a <use> tag scale like a regular SVG?为什么带有 <use> 标签的 SVG 不像常规 SVG 那样缩放?
【发布时间】:2021-02-14 00:48:24
【问题描述】:

我有一个 SVG 图标,我为它设置了一个固定的高度(例如 50 像素),但我希望它的宽度是自动的,也就是说,它需要什么取决于图标。 (很常见的情况,对吧?)。

现在,我一直在努力解决的问题是,我打算使用 &lt;symbol&gt; 标记来定义它,而不是在 HTML 中嵌入内联 SVG,然后使用它来引用它&lt;use href="... 标签,这样做显然需要我设置固定宽度和固定高度,否则它将始终默认为大约 150 像素,而不仅仅是默认为图标的宽度;当您直接嵌入 SVG 时,情况并非如此,您可以在以下两个 sn-ps 中看到所有这些:

直接嵌入SVG:(按预期工作,宽度与图标宽度一致)

.icon {
  height: 50px;
  width: auto;

  /* Aesthetics: */
  background-color: gray;
  border-radius: 5px;
}
<svg class="icon" viewBox="0 0 22.832 27.398">
    <g transform="translate(-42.667)"> <g transform="translate(42.667)"> <path class="a" d="M62.074,9.133V7.991a7.991,7.991,0,1,0-15.982,0V9.133a3.429,3.429,0,0,0-3.425,3.425V23.973A3.429,3.429,0,0,0,46.092,27.4H62.074A3.429,3.429,0,0,0,65.5,23.973V12.557A3.429,3.429,0,0,0,62.074,9.133Zm-13.7-1.142a5.708,5.708,0,0,1,11.416,0V9.133H48.375ZM63.216,23.973a1.143,1.143,0,0,1-1.142,1.142H46.092a1.143,1.143,0,0,1-1.142-1.142V12.557a1.143,1.143,0,0,1,1.142-1.142H62.074a1.143,1.143,0,0,1,1.142,1.142Z" transform="translate(-42.667)"></path> </g> <g transform="translate(50.658 13.128)"> <path class="a" d="M195.425,245.333a3.416,3.416,0,0,0-1.142,6.639v1.922a1.142,1.142,0,1,0,2.283,0v-1.922a3.416,3.416,0,0,0-1.142-6.639Zm0,4.566a1.142,1.142,0,1,1,1.142-1.142A1.143,1.143,0,0,1,195.425,249.9Z" transform="translate(-192 -245.333)"></path> </g> </g>
</svg>

但是使用&lt;use&gt; 标签:(宽度不知从何而来!)

.icon {
  height: 50px;
  width: auto;

  /* Aesthetics: */
  background-color: gray;
  border-radius: 5px;
}
<svg style="display: none;">
    <symbol id="lock" viewBox="0 0 22.832 27.398">
        <g transform="translate(-42.667)"> <g transform="translate(42.667)"> <path class="a" d="M62.074,9.133V7.991a7.991,7.991,0,1,0-15.982,0V9.133a3.429,3.429,0,0,0-3.425,3.425V23.973A3.429,3.429,0,0,0,46.092,27.4H62.074A3.429,3.429,0,0,0,65.5,23.973V12.557A3.429,3.429,0,0,0,62.074,9.133Zm-13.7-1.142a5.708,5.708,0,0,1,11.416,0V9.133H48.375ZM63.216,23.973a1.143,1.143,0,0,1-1.142,1.142H46.092a1.143,1.143,0,0,1-1.142-1.142V12.557a1.143,1.143,0,0,1,1.142-1.142H62.074a1.143,1.143,0,0,1,1.142,1.142Z" transform="translate(-42.667)" /> </g> <g transform="translate(50.658 13.128)"> <path class="a" d="M195.425,245.333a3.416,3.416,0,0,0-1.142,6.639v1.922a1.142,1.142,0,1,0,2.283,0v-1.922a3.416,3.416,0,0,0-1.142-6.639Zm0,4.566a1.142,1.142,0,1,1,1.142-1.142A1.143,1.143,0,0,1,195.425,249.9Z" transform="translate(-192 -245.333)" /> </g> </g>
    </symbol>
</svg>

<svg class="icon">
  <use href="#lock"></use>
</svg>

我已经检查了this question,以及this one,并且我意识到通过将图标的“viewBox”添加到引用的SVG,问题将得到解决,如下所示:

.icon {
  height: 50px;
  width: auto;

  /* Aesthetics: */
  background-color: gray;
  border-radius: 5px;
}
<svg style="display: none;">
    <symbol id="lock" viewBox="0 0 22.832 27.398">
        <g transform="translate(-42.667)"> <g transform="translate(42.667)"> <path class="a" d="M62.074,9.133V7.991a7.991,7.991,0,1,0-15.982,0V9.133a3.429,3.429,0,0,0-3.425,3.425V23.973A3.429,3.429,0,0,0,46.092,27.4H62.074A3.429,3.429,0,0,0,65.5,23.973V12.557A3.429,3.429,0,0,0,62.074,9.133Zm-13.7-1.142a5.708,5.708,0,0,1,11.416,0V9.133H48.375ZM63.216,23.973a1.143,1.143,0,0,1-1.142,1.142H46.092a1.143,1.143,0,0,1-1.142-1.142V12.557a1.143,1.143,0,0,1,1.142-1.142H62.074a1.143,1.143,0,0,1,1.142,1.142Z" transform="translate(-42.667)" /> </g> <g transform="translate(50.658 13.128)"> <path class="a" d="M195.425,245.333a3.416,3.416,0,0,0-1.142,6.639v1.922a1.142,1.142,0,1,0,2.283,0v-1.922a3.416,3.416,0,0,0-1.142-6.639Zm0,4.566a1.142,1.142,0,1,1,1.142-1.142A1.143,1.143,0,0,1,195.425,249.9Z" transform="translate(-192 -245.333)" /> </g> </g>
    </symbol>
</svg>

<svg class="icon" viewBox="0 0 22.832 27.398"> <!-- Copy-pasted the viewbox here -->
  <use href="#lock"></use>
</svg>

但显然这样很不方便、重复、容易出错,而且确实很难写。因此,我将不胜感激任何解决方法。有没有办法将 viewBox 属性设置为“auto”或意味着“无论内部 SVG 是什么”的东西,以避免每次你想引用时都写(或复制粘贴)viewBox一个图标?

老实说,我认为每个人都会直觉地期望它像常规的嵌入式 SVG 一样工作,因为 viewBox 已经在被引用的 symbol 上设置了一次。

【问题讨论】:

  • 是否有理由在 CSS 中添加 width: 50px 对您来说不是一个有效的解决方案?它为我解决了问题
  • @RubenHelsloot 显然是的。因为并非所有具有.icon 类的 SVG。具有 1:1 的纵横比,就像我在这个问题中包含的一样。将widthheight 设置为auto 的重点是让浏览器为您计算正确的数字
  • 清除,只是想确保在深入研究之前没有忽略更简单的解决方案
  • @RubenHelsloot 顺便说一句,感谢您的编辑,但我故意将 SVG 放在一行中,因为它们不是问题的焦点。我不希望他们让代码看起来杂乱无章。
  • 我选择了一个自定义元素来创建所有 SVG,并且在 IconMeister.github.io 中不再使用 use 完全控制 SVG,并允许每个图标使用不同的 viewBox,因此您可以从任何图标中挑选图标设置(我现在准备了 7000 多个图标)

标签: html css svg


【解决方案1】:

强烈推荐:

  • 让你所有的图标都有一个一致的viewBox。例如,"0 0 24 24" 是许多图标库都使用的常用符号。这样您就不必在需要的地方找到并复制正确的viewBox。总是"0 0 24 24"

  • 向引用&lt;svg&gt; 添加一个类,您可以使用它来设置图标宽度。

<svg class="icon lock" viewBox="0 0 24 24">
  <use href="#lock"></use>
</svg>

然后在你的 CSS 中:

.icon {
  // as above
}

.icon.lock {
  width: 45px;
  height: 50px;
}

.icon.something-else {
  width: 35px;
  height: 50px;
}

只要您的图标在其 viewBox 中水平居中,一切都会正常工作。

默认大小(方形)图标不需要额外的 CSS。您只需要为非方形的添加 CSS 规则。

【讨论】:

  • 谢谢。但我很难理解这是如何解决问题的。我想在我的 CSS 中有一个自动的width(或height),因为我不想设置固定的纵横比,现在使用这种方法,每个图标都被假定为 1:1 纵横比,在那种情况下,为什么我什至需要在 CSS 中设置一个viewBox 和一个固定的width: 50px;height: 50px;?我可以在 CSS 中使用固定的宽度和高度,完全省略 viewBox;结果是一样的,不是吗?如果我在这里误解了你,请告诉我,我希望你澄清一下。
  • 无法使用&lt;use&gt; 方法获得自动宽度或高度。我建议妥协。我的建议的优点是(a)所有的 viewboxes 都是一样的,并且(b)你只需要为非方形图标添加特殊的 CSS。
【解决方案2】:

如果您不反对使用某些 JavaScript,则以下内容应该可行。加载一次,无论页面上有多少图标。

document.querySelectorAll(".icon").forEach(node => {
  const href = node.querySelector("use").href.baseVal;
  const icon = document.querySelector(href);
  const vb = icon.viewBox.baseVal;
  node.setAttribute("viewBox", `${vb.x} ${vb.y} ${vb.width} ${vb.height}`);
});
.icon {
  height: 50px;
  width: auto;
  /* Aesthetics: */
  background-color: gray;
  border-radius: 5px;
}
<svg style="display: none;">
  <symbol id="lock" viewBox="0 0 22.832 27.398">
    <g transform="translate(-42.667)">
      <g transform="translate(42.667)">
        <path
          class="a"
          d="M62.074,9.133V7.991a7.991,7.991,0,1,0-15.982,0V9.133 
             a3.429,3.429,0,0,0-3.425,3.425V23.973A3.429,3.429,0,0,0,46.092,27.4
             H62.074A3.429,3.429,0,0,0,65.5,23.973V12.557
             A3.429,3.429,0,0,0,62.074,9.133Z
             m-13.7-1.142a5.708,5.708,0,0,1,11.416,0V9.133H48.375Z
             M63.216,23.973a1.143,1.143,0,0,1-1.142,1.142H46.092
             a1.143,1.143,0,0,1-1.142-1.142V12.557a1.143,1.143,0,0,1,1.142-1.142
             H62.074a1.143,1.143,0,0,1,1.142,1.142Z"
          transform="translate(-42.667)">
        </path>
      </g>
      <g transform="translate(50.658 13.128)">
        <path
          class="a" 
           d="M195.425,245.333a3.416,3.416,0,0,0-1.142,6.639v1.922
              a1.142,1.142,0,1,0,2.283,0v-1.922a3.416,3.416,0,0,0-1.142-6.639Z
              m0,4.566a1.142,1.142,0,1,1,1.142-1.142
              A1.143,1.143,0,0,1,195.425,249.9Z"
           transform="translate(-192 -245.333)">
        </path>
      </g>
    </g>
  </symbol>
</svg>

<svg class="icon">
  <use href="#lock"></use>
</svg>

如果您想动态添加图标 - 在页面加载后,以下也将是添加手动 viewBox 的改进:

function iconLoaded(event) {
  const node = event.target;
  const href = node.querySelector("use").href.baseVal;
  const icon = document.querySelector(href);
  const vb = icon.viewBox.baseVal;
  node.setAttribute("viewBox", `${vb.x} ${vb.y} ${vb.width} ${vb.height}`);
}
.icon {
  height: 50px;
  width: auto;
  /* Aesthetics: */
  background-color: gray;
  border-radius: 5px;
}
<svg style="display: none;">
  <symbol id="lock" viewBox="0 0 22.832 27.398">
    <g transform="translate(-42.667)">
      <g transform="translate(42.667)">
        <path
          class="a"
          d="M62.074,9.133V7.991a7.991,7.991,0,1,0-15.982,0V9.133 
             a3.429,3.429,0,0,0-3.425,3.425V23.973A3.429,3.429,0,0,0,46.092,27.4
             H62.074A3.429,3.429,0,0,0,65.5,23.973V12.557
             A3.429,3.429,0,0,0,62.074,9.133Z
             m-13.7-1.142a5.708,5.708,0,0,1,11.416,0V9.133H48.375Z
             M63.216,23.973a1.143,1.143,0,0,1-1.142,1.142H46.092
             a1.143,1.143,0,0,1-1.142-1.142V12.557a1.143,1.143,0,0,1,1.142-1.142
             H62.074a1.143,1.143,0,0,1,1.142,1.142Z"
          transform="translate(-42.667)">
        </path>
      </g>
      <g transform="translate(50.658 13.128)">
        <path
          class="a" 
           d="M195.425,245.333a3.416,3.416,0,0,0-1.142,6.639v1.922
              a1.142,1.142,0,1,0,2.283,0v-1.922a3.416,3.416,0,0,0-1.142-6.639Z
              m0,4.566a1.142,1.142,0,1,1,1.142-1.142
              A1.143,1.143,0,0,1,195.425,249.9Z"
           transform="translate(-192 -245.333)">
        </path>
      </g>
    </g>
  </symbol>
</svg>

<svg class="icon" onload="iconLoaded(event)">
  <use href="#lock"></use>
</svg>

【讨论】:

  • +1 谢谢。但是,如果&lt;use&gt; 标记上的href 属性指向外部SVG 文件(这实际上是我需要的),则它不起作用,我在我的问题中将它指向同一文档中的一个元素只是为了简单起见。
  • 啊,但是在这种情况下,为什么不简单地使用&lt;img src="my-external-svg-file.svg/&gt;
  • 因为我使用的是 SVG sprite,这意味着我有一个 SVG 文件,其中包含所有需要的图标,每个图标都是自己的 &lt;symbol&gt; 和唯一的 id,然后我在 HTML 中引用带有&lt;use&gt; 标签的文档。您不能使用带有&lt;img&gt; 的精灵(据我所知),您必须为每个图标和内容使用带有特定background-positions 的CSS background-images,这将是一个整体主题。
猜你喜欢
  • 2014-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-18
  • 1970-01-01
  • 1970-01-01
  • 2013-01-24
相关资源
最近更新 更多