为啥这个 SVG 蒙版动画在 Firefox 中断断续续,但在 Chrome 中却很流畅?

Posted

技术标签:

【中文标题】为啥这个 SVG 蒙版动画在 Firefox 中断断续续,但在 Chrome 中却很流畅?【英文标题】:Why is this SVG mask animation choppy in Firefox but smooth in Chrome?为什么这个 SVG 蒙版动画在 Firefox 中断断续续,但在 Chrome 中却很流畅? 【发布时间】:2020-10-16 02:31:02 【问题描述】:

我已经将我正在处理的 SVG 动画(应该看起来像一个装满的容器)简化为下面的示例,它在 Chrome 中运行流畅,但在 Firefox 中不稳定/断断续续。这是一个三层的 SVG:第一层是<mask>,最后一层是红色圆圈。 SVG 的中间层是一个灰色的圆圈。所以红色圆圈位于灰色圆圈的顶部,并通过通过 CSS 生成动画的蒙版可见:

#color-mask 
  fill: white;


#color-mask path 
  animation: waves .75s infinite linear;


@keyframes waves 
  from 
    transform: translateX(17rem);
  
  to 
    transform: translateX(-17rem);
  


#color-mask g 
  animation: raise 6s infinite ease-in-out;
  animation-direction: alternate;


@keyframes raise 
  from 
    transform: translateY(11rem);
  
  to 
    transform: translateY(-18rem);
  
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"   viewBox="0 0 400 400">
    <mask id="color-mask">
        <g>
            <path d="m 909.1,353.4 0,-67.6 c -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71.1,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 l 0,632.2 1419,0 z"/>
        </g>
    </mask>
    <g id="grey">
        <circle  id="top_grey" style="fill: rgb(180, 180, 180);" cx="200" cy="200" r="200"></circle>
    </g>
    <g id="color" mask="url(#color-mask)">
        <circle id="top_color" style="fill: rgb(196, 3, 3);" cx="200" cy="200" r="200"></circle>
    </g>
</svg>

CSS 动画水平和垂直翻译&lt;mask&gt;,但速度不同。

我也尝试使用 &lt;clipPath&gt; 而不是 &lt;mask&gt; 并获得相同的结果。我在 Windows 和 Linux 上的 Firefox 中得到了相同的断断续续/卡顿的结果。

我在 Firefox 中注意到的一个非常奇怪的问题是,如果我打开了开发工具,动画有时会流畅运行。 Firefox 的开发工具似乎也没有显示任何问题,但我不是 SVG 动画方面的专家。为什么 Firefox 会因此而窒息而 Chrome 不会?

【问题讨论】:

对不同的想法持开放态度,或者您想知道为什么会出现问题? @TemaniAfif 两者都很好;) 我只能给出一个 ;) 对于另一个我会说这是一个错误,或者 Firefox 可能没有完全实现掩码转换 如果你想在 Firefox 中为蒙版制作动画,最好使用 SVG 转换并在 SMIL 中制作动画。 归档于bugzilla.mozilla.org/show_bug.cgi?id=1693016,来自@Kwantuum 答案的观察。 【参考方案1】:

我花了比我想要的更多的时间来研究这个问题,我遇到的问题无法通过重新思考动画本身来轻松解决。事实证明,罪魁祸首是 Firefox 会限制屏幕外元素上的动画,这包括遮罩和剪辑路径。不幸的是,您无法自行定位 clipPaths 或其中的元素,因此我发现的最佳解决方案是将 svg 拆分为

两个:一个用于clipPath,您使用position: fixed 定位以便它始终在屏幕上(不呈现),另一个用于将引用clipPath 的实际内容。

不幸的是,您还必须考虑由动画转换的 SVG 的来源,如果您的动画会将其转换到屏幕外,那么您将再次遇到问题。在您的特定示例中,如果您将 SVG 放置在距页面顶部至少 18rem 且距左边缘至少 17rem 的位置,只要页面的左上角可见,动画就会流畅播放。

当有效的左上角蒙版元素坐标穿过实际页面视口的边界时,演示动画蒙版在 Firefox 中(显然)出现卡顿的测试用例。 (在“整页”或attachment from bugreport 1693016 中查看):

<!doctype html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SVG mask animation crossing viewport boundary Firefox stutter test</title>
<style>

html  background: dimgray; color: snow; height: 1000px; width: 1000px; 

/*

https://***.com/questions/62578638/why-is-this-svg-mask-animation-choppy-in-firefox-but-smooth-in-chrome

*/

.raise 
  animation: raise 3s infinite ease-in-out alternate;


@keyframes raise 
  from 
    transform: translateY(50%);
  
  to 
    transform: translateY(-50%);
  


svg  border: 2px solid lime;
.stutters-in-fx  border-color: red; 

text  fill: #000; stroke: #fff; stroke-width: .5; 

</style>
<script>

</script>

<svg class="stutters-in-fx" version="1.1" xmlns="http://www.w3.org/2000/svg"   viewBox="0 0 200 400">
    <mask id="color-mask">
        <rect class="raise" fill="white"   />
    </mask>
    <g id="color" mask="url(#color-mask)">
        <rect   />
    </g>
    <text dx="10%" dy="10%">↓</text>
</svg>

<svg transform="rotate(180)" version="1.1" xmlns="http://www.w3.org/2000/svg"   viewBox="0 0 200 400">
    <mask id="color-mask3">
        <rect class="raise" fill="white"   />
    </mask>
    <g id="color" mask="url(#color-mask3)">
        <rect   />
    </g>
    <text dx="10%" dy="10%">↓</text>
</svg>

<br>

<svg class="stutters-in-fx" transform="rotate(-90)" transform-origin="100 100" version="1.1" xmlns="http://www.w3.org/2000/svg"   viewBox="0 0 200 400">
    <mask id="color-mask2">
        <rect class="raise" fill="white"   />
    </mask>
    <g id="color" mask="url(#color-mask2)">
        <rect   />
    </g>
    <text dx="10%" dy="10%">↓</text>
</svg>

<br>

<svg transform="rotate(90) translate(-400 0)" transform-origin="100 300" version="1.1" xmlns="http://www.w3.org/2000/svg"   viewBox="0 0 200 400">
    <mask id="color-mask4">
            <rect class="raise" fill="white"   />
    </mask>
    <g id="color" mask="url(#color-mask4)">
        <rect   />
    </g>
    <text dx="10%" dy="10%">↓</text>
</svg>

【讨论】:

【参考方案2】:

这是 SVG 解决方案。只需调整第一个动画的值即可获得完美的重复:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"   viewBox="0 0 400 400">
    <mask id="color-mask" fill="white">
        <g>
            <path d="m 909.1,353.4 0,-67.6 c -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71.1,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 l 0,632.2 1419,0 z">
            <animateTransform attributeName="transform" attributeType="XML" type="translate" from="495" to="0" dur="0.75s" repeatCount="indefinite"/>
            </path>
            <animateTransform attributeName="transform" attributeType="XML"  type="translate" values="0,150; 0,-300; 0,150" keyTimes="0; 0.5; 1" dur="12s" repeatCount="indefinite"/>
        </g>
    </mask>
    <g id="grey">
        <circle  id="top_grey" style="fill: rgb(180, 180, 180);" cx="200" cy="200" r="200"></circle>
    </g>
    <g id="color" mask="url(#color-mask)">
        <circle id="top_color" style="fill: rgb(196, 3, 3);" cx="200" cy="200" r="200"></circle>
    </g>
</svg>

【讨论】:

【参考方案3】:

使用 CSS 掩码的不同想法,您不会遇到问题。我使用您提供的路径作为掩码。只需确保为 viewBox 设置正确的值

.box 
  display:inline-flex;
  width:300px;
  background: rgb(180, 180, 180);
  border-radius:50%;
  position:relative;
  overflow:hidden;

.box:after 
  content:"";
  padding-top:100%;

.box:before 
  content:"";
  position:absolute;
  left:0%;
  width:200%;
  height:30%;
  bottom:-10%;
  background:rgb(196, 3, 3);
  -webkit-mask:url('data:image/svg+xml;utf8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg"  viewBox="-500 0 1100 900"><path d="m 909.1,353.4 0,-67.6 c -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71.1,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 l 0,632.2 1419,0 z"/></svg>') top/100% auto;
          mask:url('data:image/svg+xml;utf8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg"  viewBox="-500 0 1200 900"><path d="m 909.1,353.4 0,-67.6 c -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71.1,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 l 0,632.2 1419,0 z"/></svg>') top/100% auto; 
   animation:raise 6s infinite ease-in-out alternate,waves .75s infinite linear;

@keyframes waves 
  to 
    transform: translateX(-50%);
  

@keyframes raise 
  to 
    height:160%;
  
<div class="box">
</div>
<div class="box" style="width:200px;">
</div>

要仅使用转换,您可以添加一个额外的元素:

.box 
  width:300px;
  display:inline-block;
  background: rgb(180, 180, 180);
  border-radius:50%;
  overflow:hidden;

.box div 
  padding-top:100%;
  position:relative;    
  animation: raise 6s infinite ease-in-out alternate;

.box div:before 
  content:"";
  position:absolute;
  left:0%;
  width:200%;
  height:160%;
  bottom:-10%;
  background:rgb(196, 3, 3);
  -webkit-mask:url('data:image/svg+xml;utf8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg"  viewBox="-500 0 1100 900"><path d="m 909.1,353.4 0,-67.6 c -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71.1,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 l 0,632.2 1419,0 z"/></svg>') top/100% auto;
          mask:url('data:image/svg+xml;utf8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg"  viewBox="-500 0 1200 900"><path d="m 909.1,353.4 0,-67.6 c -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 -71.1,0 -107.2,14.8 -142.1,29 -34.8,14.2 -70.9,29 -141.7,29 -70.8,0 -106.9,-14.7 -141.7,-29 -34.9,-14.3 -71,-29 -142.1,-29 l 0,632.2 1419,0 z"/></svg>') top/100% auto; 
   animation:waves .75s infinite linear;

@keyframes waves 
  to 
    transform: translateX(-50%);
  

@keyframes raise 
  from 
    transform:translateY(120%);
  
<div class="box">
<div></div>
</div>
<div class="box" style="width:200px;">
<div></div>
</div>

【讨论】:

伟大的工作。我可能最终会使用它,因为它基本上解决了问题。只是希望我知道为什么 FF 对我所拥有的东西感到窒息。 @j08691 等待罗伯特,他是该领域的专家,一定会告诉你原因 Firefox 中对掩码的 CSS 转换支持是最近才出现的,我想我们仍然存在一些问题。

以上是关于为啥这个 SVG 蒙版动画在 Firefox 中断断续续,但在 Chrome 中却很流畅?的主要内容,如果未能解决你的问题,请参考以下文章

旋转 SVG 蒙版

悬停时动画 SVG 图像蒙版

Firefox 浏览器中的 SVG 路径元素缩放转换错误

需要帮助清理 SVG 动画

为啥这个嵌套的 SVG 在 Firefox 中不起作用?

为啥这个 SVG 在 Chrome 和 Firefox 中都缺少一半的轮廓?