为啥这个 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 动画水平和垂直翻译<mask>
,但速度不同。
我也尝试使用 <clipPath>
而不是 <mask>
并获得相同的结果。我在 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 中却很流畅?的主要内容,如果未能解决你的问题,请参考以下文章