This recent question触及 <svg> 元素上的filter和viewBox属性如何交互 . 乍一看,Paul LeBeau的回答似乎很明显:首先应用过滤器,然后将viewBox的隐含变换应用于结果 .

但是,搜索规范并没有提出明确的答案,浏览器的实施方式也各不相同 . 我设计了一个测试用例,它还检查了同一元素上的另一个 transform 属性:

<svg filter="url(#filter)" width="50" height="50"
     viewBox="0 0 10 10" transform="scale(-1 1)">

事实证明,最外层的 <svg> 元素和嵌套的 <svg> 元素之间的处理存在差异 - 而浏览器不同意应该是什么区别:

<svg width="50" height="50">
  <svg filter="url(#filter)" width="50" height="50"
       viewBox="0 0 10 10" transform="translate(100) scale(-1 1)">

transform 属性在SVG 2中是新的,我把它们包括在内,因为它们的存在使得更容易弄清楚浏览器在做什么 . )

这是带有内联SVG的完整测试用例;独立文件的结果是相同的:

svg {
    overflow:visible;
}
table {
    border-spacing: 100px 60px;
}
td {
    padding: 0;
    max-width: 100px;
    height: 75px;
    vertical-align: top;
    font-size: 11px;
}
<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
  <filter id="filter" filterUnits="userSpaceOnUse" x="-25" y="0" width="75" height="75">
    <feOffset dx="-5" dy="5" result="bottomright" />
    <feFlood flood-color="red" result="color" />
    <feComposite in="color" in2="bottomright" operator="in" result="shadow" />
    <feFlood flood-color="lightgrey" flood-opacity="0.5" result="bg" />
    <feMerge>
      <feMergeNode in="bg" />
      <feMergeNode in="shadow" />
      <feMergeNode in="SourceGraphic" />
    </feMerge>
  </filter>
  <filter id="filter2" filterUnits="objectBoundingBox" x="-50%" y="0%" width="150%" height="150%">
    <feOffset dx="-5" dy="5" result="bottomright" />
    <feFlood flood-color="red" result="color" />
    <feComposite in="color" in2="bottomright" operator="in" result="shadow" />
    <feFlood flood-color="lightgrey" flood-opacity="0.5" result="bg" />
    <feMerge>
      <feMergeNode in="bg" />
      <feMergeNode in="shadow" />
      <feMergeNode in="SourceGraphic" />
    </feMerge>
  </filter>
</svg>
<table>
  <tr>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg"
           style="filter:url(#filter);fill:green"
           viewBox="0 0 10 10" width="50" height="50">
        <rect width="10" height="10" />
      </svg>
    </td>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg"
           style="filter:url(#filter2);fill:green"
           viewBox="0 0 10 10" width="50" height="50">
        <rect width="10" height="10" />
      </svg>
    </td>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg"
           style="filter:url(#filter);fill:green"
           transform="scale(-1 1)" width="10" height="10">
        <rect width="10" height="10" />
      </svg>
    </td>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg"
           style="filter:url(#filter2);fill:green"
           transform="scale(-1 1)" width="10" height="10">
        <rect width="10" height="10" />
      </svg>
    </td>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg"
           style="filter:url(#filter);fill:green"
           viewBox="0 0 10 10" transform="scale(-1 1)" width="50" height="50">
        <rect width="10" height="10" />
      </svg>
    </td>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg"
           style="filter:url(#filter2);fill:green"
           viewBox="0 0 10 10" transform="scale(-1 1)" width="50" height="50">
        <rect width="10" height="10" />
      </svg>
    </td>
  </tr>
  <tr>
    <td>outer &lt;svg viewBox&gt; userSpaceOnUse</td>
    <td>outer &lt;svg viewBox&gt; objectBoundingBox</td>
    <td>outer &lt;svg transform&gt; userSpaceOnUse</td>
    <td>outer &lt;svg transform&gt; objectBoundingBox</td>
    <td>outer &lt;svg transform viewBox&gt; userSpaceOnUse</td>
    <td>outer &lt;svg transform viewBox&gt; objectBoundingBox</td>
  </tr>
  <tr>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
        <svg style="filter:url(#filter);fill:green"
             viewBox="0 0 10 10" width="50" height="50">
          <rect width="10" height="10" />
        </svg>
      </svg>
    </td>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
        <svg style="filter:url(#filter2);fill:green"
             viewBox="0 0 10 10" width="50" height="50">
          <rect width="10" height="10" />
        </svg>
      </svg>
    </td>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
        <svg style="filter:url(#filter);fill:green"
             transform="translate(10) scale(-1 1)" width="10" height="10">
          <rect width="10" height="10" />
        </svg>
      </svg>
    </td>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
        <svg style="filter:url(#filter2);fill:green"
             transform="translate(10) scale(-1 1)" width="10" height="10">
          <rect width="10" height="10" />
        </svg>
      </svg>
    </td>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
        <svg style="filter:url(#filter);fill:green"
             viewBox="0 0 10 10" transform="translate(100) scale(-1 1)" width="50" height="50">
          <rect width="10" height="10" />
        </svg>
      </svg>
    </td>
    <td>
      <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
        <svg style="filter:url(#filter2);fill:green"
             viewBox="0 0 10 10" transform="translate(100) scale(-1 1)" width="50" height="50">
          <rect width="10" height="10" />
        </svg>
      </svg>
    </td>
  </tr>
  <tr>
    <td>nested &lt;svg viewBox&gt; userSpaceOnUse</td>
    <td>nested &lt;svg viewBox&gt; objectBoundingBox</td>
    <td>nested &lt;svg transform&gt; userSpaceOnUse</td>
    <td>nested &lt;svg transform&gt; objectBoundingBox</td>
    <td>nested &lt;svg transform viewBox&gt; userSpaceOnUse</td>
    <td>nested &lt;svg transform viewBox&gt; objectBoundingBox</td>
  </tr>
</table>

我一直试图推断出不同浏览器使用的处理算法 . 我知道有一些涉及的解释,实际上可能会有不同的实施,但基本的差异仍然存在并且脱颖而出 .

术语initial coordinates是描述为的那个

对于最外面的svg元素,SVG用户代理必须确定初始视口坐标系和初始局部坐标系,使得两个坐标系是相同的 . 两个坐标系的原点必须位于SVG视口的原点,初始坐标系中的一个单位必须等于SVG视口中的一个CSS px .

术语viewBox coordinates是应用描述为的变换得到的

viewBox属性的存在会导致应用于视口坐标系的变换,如计算SVG视口的等效变换中所述 .

Firefox

对于外部 svg 元素

  • 将内容转换为 initial 坐标

  • 确定 initial 坐标中的滤镜效果区域值

  • render filterinitial 坐标中的原始值

  • 剪辑渲染内容以在 initial 坐标中过滤效果区域

  • transform 属性应用于结果

对于嵌套的 svg 元素

  • 确定 viewBox 坐标中的滤镜效果区域值

  • 将内容转换为 initial 坐标

  • render filterinitial 坐标中包含原始值

  • 剪辑渲染内容以在 initial 坐标中过滤效果区域

  • transform 属性应用于结果

Chrome

对于外部 svg 元素

  • 将内容转换为 initial 坐标

  • 确定 initial 坐标中的滤镜效果区域值

  • render filterinitial 坐标中的原始值

  • 剪辑渲染内容以过滤 initial 坐标中的效果区域

  • transform 属性应用于结果

对于嵌套的 svg 元素

  • 确定 viewBox 坐标中的滤镜效果区域值

  • render filterviewBox 坐标中包含原始值

  • 剪辑渲染内容以在 viewBox 坐标中过滤效果区域

  • 将结果转换为 initial 坐标

  • 忽略 transform 属性

边缘

对于外部 svg 元素,不应用过滤器 .

对于嵌套的 svg 元素

  • 确定 viewBox 坐标中的滤镜效果区域值

  • render filterviewBox 坐标中的原始值

  • 剪辑渲染内容以过滤 viewBox 坐标中的效果区域

  • 将结果转换为 initial 坐标

  • 忽略 transform 属性

对于外部 svg 元素,浏览器(Edge无法应用它们)同意在 viewBox 隐式缩放后将滤镜应用于元素,并且滤镜效果区域的剪切发生在相同的坐标系 . 过滤后, transform 属性将应用于结果 .

对于嵌套的 svg 元素,Firefox会将过滤器应用于缩放元素,但过滤器效果区域的剪切仍然未缩放,并剪切掉大部分缩放内容 .

Chrome和Edge会更改嵌套 svg 元素的处理顺序并应用过滤器和剪切到viewBox坐标系中的滤镜效果区域 . 然后仅应用隐式转换,扩展内容和效果区域 . 嵌套的 <svg> 元素上的 transform 属性将被忽略 .

SVG 2草案

处理订单有one explicit description

分组元素,例如'g'元素(请参阅容器元素)创建合成组...因此,渲染合成组遵循以下步骤:如果组是隔离的:初始背景设置为初始化的新缓冲区with rgba(0,0,0,0)作为图形元素或'g'元素的组的内容按顺序呈现在初始背景滤镜上,并应用修改组画布的其他效果以提供高质量必须在操作坐标空间中应用渲染,滤镜基元和其他位图效果 . 应用组变换组合画布与组背景混合并合成

虽然没有明确提及 <svg> 元素,但链接的"container elements"词汇表术语列出了它们,并且further down这是关于合成的主题:

根据合成和混合规范,<svg>元素始终创建一个隔离组 .

当变换是 viewBox 的结果时,我没有发现处理顺序没有明确提及,步骤4似乎只考虑了 transform 属性 . 此外,似乎没有描述外部和嵌套 <svg> 元素之间的区别 .

以下Annotation关于草案中正在进行的讨论处理 transform 的使用:

由于viewBox属性,对<svg>元素的转换有点特殊 . 应该应用变换,就像<svg>具有包含该变换集的父元素一样 . 解决方案:transform属性在概念上应用于<svg>元素的外部,并且表示属性和样式属性之间没有区别(就可视结果而言) .

滤镜效果模块1级草稿

它仅将SVG 1.1引用为interacting module,并且仅在filter effects region的定义上引用"user coordinate system":

如果filterUnits等于userSpaceOnUse,则x,y,width,height表示当引用<filter>元素时当前用户坐标系中的值(即,引用过滤器元素的元素的用户坐标系) ...)如果filterUnits等于objectBoundingBox,则x,y,width,height表示引用元素上的边界框的分数或百分比...

关于filter primitive units,它使用术语"local coordinate system",它在CSS转换的链接词汇表中等同于"user coordinate system":

如果primitiveUnits等于userSpaceOnUse,则过滤器定义中的任何长度值表示引用过滤器元素时当前本地坐标系中的值(即,引用<filter>元素的元素的用户坐标系) ...)如果primitiveUnits等于objectBoundingBox,则过滤器定义中的任何长度值表示引用元素上的边界框的分数或百分比...

所有这一切都让我有以下问题:

  • 可以从SVG 2规范确定明确的处理顺序吗?这个订单是什么?

  • 外部和嵌套 <svg> 元素的处理顺序是否不同?怎么样?