【问题标题】:why is size of text used as source in an SVG feComposite filter affected by the svg element size?为什么在 SVG feComposite 过滤器中用作源的文本大小受 svg 元素大小的影响?
【发布时间】:2021-03-15 02:47:08
【问题描述】:

我正在尝试学习如何在 SVG 中使用 feComposite,并且特别想使用文本作为合成源之一。这是我正在尝试做的初步示例。

<svg width="100" height="100">

  <defs>
    <circle id="circ" cx="50" cy="50" r="40" stroke-width="0" fill="black" />
    <text id="A" x="35" y="70" fill="black" style="font-size:60; font-family:Arial; font-weight:700">8</text>
    
    <filter id="myfilter" width="120%">
      <feImage xlink:href="#circ" result="lay1"/>
      <feImage xlink:href="#A" result="lay2"/>
      <feComposite operator="out" in="lay1" in2="lay2" result="COMP"/>
    </filter>
  </defs>

  <g filter="url(#myfilter)" >
    <use href="#circ"/>
    <use href="#A"/>
  </g>

</svg> 

正如预期的那样,它给了我这个结果:

但是,后来我想让一切都变得更大。所以,我需要增加 svg 元素的宽度和高度。但是,当我这样做时,它会导致文本变小。这里是修改后的 SVG,只增加了 svg 元素的 height 属性:

<svg width="100" height="150">

  <defs>
    <circle id="circ" cx="50" cy="50" r="40" stroke-width="0" fill="black" />
    <text id="A" x="35" y="70" fill="black" style="font-size:60; font-family:Arial; font-weight:700">8</text>
    
    <filter id="myfilter" width="120%">
      <feImage xlink:href="#circ" result="lay1"/>
      <feImage xlink:href="#A" result="lay2"/>
      <feComposite operator="out" in="lay1" in2="lay2" result="COMP"/>
    </filter>
  </defs>

  <g filter="url(#myfilter)" >
    <use href="#circ"/>
    <use href="#A"/>
  </g>

</svg> 

这导致文本内容在垂直方向上缩小。

如果我增加 svg 元素的宽度,那么文本会横向缩小:

<svg width="150" height="150">

  <defs>
    <circle id="circ" cx="50" cy="50" r="40" stroke-width="0" fill="black" />
    <text id="A" x="35" y="70" fill="black" style="font-size:60; font-family:Arial; font-weight:700">8</text>
    
    <filter id="myfilter" width="120%">
      <feImage xlink:href="#circ" result="lay1"/>
      <feImage xlink:href="#A" result="lay2"/>
      <feComposite operator="out" in="lay1" in2="lay2" result="COMP"/>
    </filter>
  </defs>

  <g filter="url(#myfilter)" >
    <use href="#circ"/>
    <use href="#A"/>
  </g>

</svg> 

如果我不增加 svg 元素的高度或宽度,而是减少值,那么文本将在相应方向上更大

这只发生在用作过滤器源的文本上。如果我在没有过滤器的情况下使用相同的文本元素,它不会受到 svg 根元素的宽度/高度变化的影响。例如,在下面,我通过添加&lt;use&gt; 元素来修改前面的示例以添加文本的另一个实例(包装在&lt;g&gt; 中,页面下方有翻译):

<svg width="150" height="150">

  <defs>
    <circle id="circ" cx="50" cy="50" r="40" stroke-width="0" fill="black" />
    <text id="A" x="35" y="70" fill="black" style="font-size:60; font-family:Arial; font-weight:700">8</text>
    
    <filter id="myfilter" width="120%">
      <feImage xlink:href="#circ" result="lay1"/>
      <feImage xlink:href="#A" result="lay2"/>
      <feComposite operator="out" in="lay1" in2="lay2" result="COMP"/>
    </filter>
  </defs>

  <rect x="0" y="40" width="100" height="20" fill="none"/>
  
  <g filter="url(#myfilter)" >
    <use href="#circ"/>
    <use href="#A"/>
  </g>
  
  <g transform="translate(0,70)">
    <use href="#A"/>
  </g>

</svg> 

这里发生了什么?为什么作为 feComposite 源的文本会根据 svg 宽度/高度进行缩放?

【问题讨论】:

  • 这似乎是一个 Chrome 错误。一致的行为是不改变大小,因为它可以在使用 librsvg 或 Inkscape 渲染时观察到。请注意,Firefox 根本不呈现过滤器,我认为是因为不支持引用内部 id。

标签: svg svg-filters


【解决方案1】:

一般来说,当未指定尺寸和单位时,SVG 滤镜很容易出错。在这种情况下, g 元素将错误的维度传递给过滤器。比如给g元素内部的第一个use元素(圆)加上一个“y”坐标,并调整它的值,文本就会收缩和扩大。

如果您明确地为所有内容添加尺寸并通过 preserveAspectRatio 指定是否要保留输入的纵横比,则一切正常。如果您希望保留它,无论您希望它调整为输入的更大(满足)还是更小(切片)维度。

你的过滤器实际上丢弃了它被调用的内容(它的 SourceGraphic) - 所以你在 g 元素中拥有什么并不重要 - 它只是使用内容来调整过滤器区域的大小(结果不一致) .因此,您不妨将过滤器应用于 100%/100% 矩形元素 - 您应该明确调整其大小。

因此,如果您明确指定尺寸 - 此过滤器就可以正常工作。我不知道你想达到什么行为。如果您希望内容在扩展 SVG 元素宽度/高度时保持固定 - 这就是您想要的过滤器。

<svg x="0" y="0" width="100px" height="150px" >

  <defs>
    <circle id="circ" cx="50" cy="50" r="40" stroke-width="0" fill="black" />
    <text id="A" x="35" y="70" fill="black" style="font-size:60; font-family:Arial; font-weight:700">8</text>
    
    <filter id="myfilter" filterUnits="userSpaceOnUse" primitiveUnits="objectBoundingBox" x="0" y="0" width="100" height="150">
      <feImage x="0" y="0" height="1" width="1"  xlink:href="#circ" result="lay1"/>
      <feImage x="0" y="0" height="1" width="1"  xlink:href="#A" result="lay2"/>
      <feComposite operator="out" in="lay1" in2="lay2" result="COMP"/>
    </filter>
  </defs>

  <rect filter="url(#myfilter)" x="0%" y="0%" width="100%" height="100%"/>

</svg> 

    <svg x="0" y="0" width="200px" height="250px" >

      <defs>
        <circle id="circ" cx="50" cy="50" r="40" stroke-width="0" fill="black" />
        <text id="A" x="35" y="70" fill="black" style="font-size:60; font-family:Arial; font-weight:700">8</text>
        
        <filter id="myfilter" filterUnits="userSpaceOnUse" primitiveUnits="objectBoundingBox" x="0" y="0" width="100" height="150">
          <feImage x="0" y="0" height="1" width="1"  xlink:href="#circ" result="lay1"/>
          <feImage x="0" y="0" height="1" width="1"  xlink:href="#A" result="lay2"/>
          <feComposite operator="out" in="lay1" in2="lay2" result="COMP"/>
        </filter>
      </defs>

      <rect filter="url(#myfilter)" x="0%" y="0%" width="100%" height="100%"/>

    </svg> 

另一方面,如果您希望内容可以缩放,但要保持其纵横比,则需要这样的过滤器。

<svg x="0" y="0" width="100px" height="100px" viewBox="0 0 100 100" >

  <defs>
    <circle id="circ" cx="50" cy="50" r="40" stroke-width="0" fill="black" />
    <text id="A" x="35" y="70" fill="black" style="font-size:60; font-family:Arial; font-weight:700">8</text>
    
    <filter id="myfilter"  primitiveUnits="objectBoundingBox" x="0" y="0" width="1" height="1">
      <feImage x="0" y="0" height="1" width="1"  xlink:href="#circ" result="lay1" preserveAspectRatio="xMidYMid slice"/>
      <feImage x="0" y="0" height=".65 " width="1"  xlink:href="#A" result="lay2" preserveAspectRatio="xMidYMid slice"/>
      <feComposite operator="out" in="lay1" in2="lay2" result="COMP"/>
    </filter>
  </defs>

  <rect filter="url(#myfilter)" x="0" y="0" width="100" height="150"/>

</svg> 

    <svg x="0" y="0" width="200px" height="250px" viewBox="0 0 100 100" >

      <defs>
        <circle id="circ" cx="50" cy="50" r="40" stroke-width="0" fill="black" />
        <text id="A" x="35" y="70" fill="black" style="font-size:60; font-family:Arial; font-weight:700">8</text>
        
        <filter id="myfilter"  primitiveUnits="objectBoundingBox" x="0" y="0" width="1" height="1">
          <feImage x="0" y="0" height="1" width="1"  xlink:href="#circ" result="lay1" preserveAspectRatio="xMidYMid slice"/>
          <feImage x="0" y="0" height=".65 " width="1"  xlink:href="#A" result="lay2" preserveAspectRatio="xMidYMid slice"/>
          <feComposite operator="out" in="lay1" in2="lay2" result="COMP"/>
        </filter>
      </defs>

      <rect filter="url(#myfilter)" x="0" y="0" width="100" height="150"/>

    </svg> 

【讨论】:

  • 感谢您的修改。我仍然不完全理解发生了什么,我需要了解更多关于 SVG 中的单位/维度的信息。你说,“坏尺寸”;这仅仅是在导致不确定结果的意外输入的意义上(即,通过符合 SVG 运行时实现的不同处理)?或者从某种意义上说,观察到的行为是确定性的,并且在给定输入的情况下根据 SVG 规范是预期的,但不是我想要的?
  • 尺寸错误 = 错误。 SVG 过滤器中有很多错误。浏览器团队修复它们的速度非常缓慢。有一些过滤器错误已经突出了 6 年以上。 SVG 文本也有很多未完成的功能和错误。因此,过滤器非常强大,但您必须在任何地方测试所有内容并小心错误。在您的情况下,过滤器在 Firefox 中根本不起作用,因为它不支持对 feImage 的片段输入 - 您必须通过数据 uri 内联内容。
  • 是否有 feImage 的替代品让我获得 feComposite 的输入?我知道我可以使用 SourceImage 作为一个,但还没有弄清楚如何获得第二个。
  • "SourceGraphic" - 不是 "SourceImage" :) 还有其他方法 - 将两个形状并排放在 SourceGraphic 中,并使用 feOffset 覆盖它们,并使用大小调整为您想要选择的结果的 feFlood通过 feComposite/in 得到最终结果。另一种方法适用于单一颜色形状 - 您构建一个 SourceGraphic 将不同形状放入红色、绿色和蓝色通道,然后使用 feColorMatrix 将它们分离成单独的“结果”,然后再进行进一步处理。
  • 对,“SourceGraphic”。 (从内存中写入。)“过滤器在 Firefox 中根本不起作用,因为它不支持对 feImage 的片段输入 - 你必须通过数据 uri 内联内容”这是怎么做到的?
猜你喜欢
  • 2019-10-01
  • 2020-01-03
  • 2019-06-04
  • 1970-01-01
  • 1970-01-01
  • 2020-06-09
  • 2019-09-19
  • 2014-12-17
  • 2017-08-02
相关资源
最近更新 更多