SVG 2.0中<svg>元素上的过滤器的处理顺序是什么?

时间:2018-07-11 23:03:02

标签: svg css-filters

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属性的存在导致将转换应用于视口坐标系,如Computing the equivalent transform of an SVG viewport中所述。

Firefox

用于外部svg元素

  1. 将内容转换为初始坐标
  2. 初始坐标中确定滤镜效果区域值
  3. 使用初始坐标中的原始值渲染filter
  4. 剪辑呈现的内容以过滤初始坐标
  5. 中的效果区域
  6. transform属性应用于结果

用于嵌套的svg元素

  1. viewBox 坐标中确定滤镜效果区域值
  2. 将内容转换为初始坐标
  3. 使用初始坐标中的原始值渲染filter
  4. 剪辑呈现的内容以过滤初始坐标
  5. 中的效果区域
  6. transform属性应用于结果

Chrome

用于外部svg元素

  1. 将内容转换为初始坐标
  2. 初始坐标中确定滤镜效果区域值
  3. 使用初始坐标中的原始值渲染filter
  4. 剪辑呈现的内容以过滤初始坐标
  5. 中的效果区域
  6. transform属性应用于结果

用于嵌套的svg元素

  1. viewBox 坐标中确定滤镜效果区域值
  2. 使用 viewBox 坐标中的原始值渲染filter
  3. 剪辑呈现的内容以过滤 viewBox 坐标
  4. 中的效果区域
  5. 将结果转换为初始坐标
  6. 忽略transform属性

Edge

对于外部svg元素,未应用过滤器。

用于嵌套的svg元素

  1. viewBox 坐标中确定滤镜效果区域值
  2. 使用 viewBox 坐标中的原始值渲染filter
  3. 剪辑呈现的内容以过滤 viewBox 坐标
  4. 中的效果区域
  5. 将结果转换为初始坐标
  6. 忽略transform属性

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

对于嵌套的svg元素,Firefox将滤镜应用于缩放后的元素,但是对滤镜效果区域的裁剪仍未缩放,并裁剪了大多数缩放后的内容。

Chrome和Edge更改了嵌套svg元素的处理顺序,并将滤镜和裁剪应用于viewBox坐标系中的滤镜效果区域。然后才应用隐式转换,从而扩大内容和效果区域。嵌套transform元素上的<svg>属性将被忽略。

SVG 2草案

one explicit description个处理订单:

  

分组元素(例如“ g”元素(请参阅container elements)创建了一个合成组...

     

因此,渲染合成组遵循以下步骤:
  如果该组是孤立的:

     
      
  1. 初始背景设置为使用rgba(0,0,0,0)初始化的新缓冲区
  2.   
  3. 作为图形元素或“ g”元素的组内容按顺序渲染到初始背景上
  4.   应用
  5. 滤镜和其他修改组画布的效果      
        

    要提供高质量的渲染,必须在operating coordinate space中应用滤镜基元和其他位图效果。

      
  6.   
  7. 应用了组转换
  8.   
  9. 小组画布与小组背景混合并合成
  10.   

虽然没有明确提到<svg>元素,但是链接的“容器元素”术语表列出了它们,而further down则是关于合成的:

  

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

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

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

  

由于<svg>属性,viewBox元素上的转换有些特殊。应该像应用<svg>具有该转换集的父元素一样应用该转换。

     

RESOLUTION:变换属性在概念上适用于<svg>元素的外部,表示属性和样式属性之间没有区别(就视觉结果而言)。

“滤镜效果模块1级草案”

它仅将SVG 1.1引用为interacting module,并且仅在filter effects region的定义中引用了“用户坐标系”:

  

如果filterUnits等于userSpaceOnUse,则xywidthheight表示当前用户坐标系中的值放置<filter>元素时的位置(即,引用过滤器元素的元素的用户坐标系...)

     

如果filterUnits等于objectBoundingBox,则xywidthheight表示边界框上的分数或百分比引用元素...

关于filter primitive units,它使用术语“本地坐标系”,在CSS转换的链接词汇表中,该术语等同于“用户坐标系”:

  

如果primitiveUnits等于userSpaceOnUse,则过滤器定义中的任何长度值都表示引用过滤器元素时当前位置的当前local coordinate system中的值(即,引用<filter>元素的元素的用户坐标系...)

     

如果primitiveUnits等于objectBoundingBox,则过滤器定义中的任何长度值都表示引用元素上边界框的分数或百分比...

所有这些使我面临以下问题:

  1. 能否根据SVG 2规范确定明确的处理顺序?那是什么顺序?
  2. 外部元素和嵌套元素<svg>的处理顺序应该有所不同吗?怎么样?

0 个答案:

没有答案