SVG <use>上的动画在FireFox和Safari中不起作用

时间:2020-02-05 14:48:57

标签: html css svg css-animations

我有一个过渡动画,我想在group元素内的SVG元素的宽度上触发。该动画似乎只能在Chrome浏览器上运行。

<!-- works -->
<svg width="400" height="100">
  <rect id="rect-1" width="400" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
</svg>

<!-- only works in Chrome -->
<svg width="400" height="400">
  <defs>
    <g id="my-rect">
      <rect id="rect-2" width="400" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
    </g>
  </defs>

  <use xlink:href="#my-rect" y="0"/>
  <use xlink:href="#my-rect" y="110"/>
</svg>
.grow {
  -webkit-transition: 1.5s;
  -moz-transition: 1.5s;
  -o-transition: 1.5s;
  transition: 3s;
  width: 10px;
}
(() => {
  setTimeout(() => {
    const rect1 = document.getElementById('rect-1');
    rect1.classList.add('grow');

    const rect2 = document.getElementById('rect-2');
    rect2.classList.add('grow');
  }, 1000);
})();

要复制,请在Safari或Firefox中打开此fiddle。 您将看到第二个矩形的过渡无法正常工作。

是否有任何变通方法来使SVG组中特定元素的过渡动画正常工作?

3 个答案:

答案 0 :(得分:2)

使用:

该元素从SVG文档中获取节点,并且 将它们复制到其他地方。 -mdn

因此不必使用use,因为您不必复制它。

(() => {
  setTimeout(() => {
    const rect1 = document.getElementById('rect-1');
    rect1.classList.add('grow');

    const rect2 = document.getElementById('rect-2');
    rect2.classList.add('grow');

  }, 1000);
})();
.grow {
  -webkit-transition: 1.5s;
  -moz-transition: 1.5s;
  -o-transition: 1.5s;
  transition: width 3s;
  width: 10px;
}
<!-- works -->
<svg width="400" height="100">
  <rect id="rect-1" width="400" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
</svg>

<svg width="400" height="100">
  <rect id="rect-2" width="400" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
</svg>

更新1

当元素重复时,即使SVG <animate>在FireFox中也不起作用。虽然如果未在刷新的元素内使用。这是一个已知的问题。关于堆栈溢出,有多个未解决的问题,例如this one

<!-- Learn about this code on MDN: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use -->

<svg viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
  <circle id="myCircle" cx="5" cy="5" r="4" stroke="blue"/>
  <use href="#myCircle" x="10" fill="blue"/>
  <use href="#myCircle" x="20" fill="white" stroke="red"/>
  <!--
stroke="red" will be ignored here, as stroke was already set on myCircle. 
Most attributes (except for x, y, width, height and (xlink:)href)
do not override those set in the ancestor.
That's why the circles have different x positions, but the same stroke value.
  -->
  
      <animate 
           xlink:href="#myCircle"
           attributeName="r"
           from="4"
           to="2" 
           dur="5s"
           begin="0s"
           repeatCount="1"
           fill="freeze" 
           id="circ-anim"/>
           
           
</svg>

更新2

被引用元素上的动画会导致实例也被动画化。 use - w3

对于每个文档,如果您为刷新的元素(`myCircle)设置动画,则也应该对所有重复元素进行动画处理。因此,我想这是一个错误,无法在Firefox和Safari中使用。

更新3-解决方法

使用svg <animate>标签并将其包含在<rect>中。

<svg width="400" height="400">
  <defs>
    <g id="my-rect">
      <rect id="rect-2" width="400" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)">
        <animate 
                 attributeName="width"
                 from="400"
                 to="10" 
                 dur="3s"
                 begin="1s"
                 repeatCount="1"
                 fill="freeze" 
                 id="rect-2"/>

      </rect>
    </g>
  </defs>

  <use xlink:href="#my-rect" y="0"/>
  <use xlink:href="#my-rect" y="110"/>
</svg>

答案 1 :(得分:1)

如何将动画标签嵌套在圆内?

<svg width="400" height="350">
    <rect id="my-rect" width="400" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)">
        <animate 
               attributeName="width"
               from="0"
               to="400" 
               dur="5s"
               begin="0s"
               repeatCount="1"
               fill="freeze" 
               id="rect-anim"/>
    </rect>

    <use xlink:href="#my-rect" y="110"/>
    <use xlink:href="#my-rect" y="220"/>
     
</svg>

答案 2 :(得分:0)

总结来说,一种解决方法是退回到SMIL SVG动画而不使用CSS。

请参阅针对我的原始问题改编的fiddle

<svg width="400" height="100">
  <rect id="rect-1" width="400" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
</svg>

<svg width="400" height="400">
  <defs>
    <g id="my-rect">
      <rect id="g-rect" width="400" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)"/>
    </g>
  </defs>

  <use xlink:href="#my-rect" y="0"/>
  <use xlink:href="#my-rect" y="110"/>
</svg>
(() => {
  setTimeout(() => {
    const rect1 = document.getElementById('rect-1');
    rect1.classList.add('grow');

    const groupRect = document.getElementById('g-rect');

    const growAnimation = document.createElementNS('http://www.w3.org/2000/svg', 'animate')
    growAnimation.setAttribute('attributeName', 'width');
    growAnimation.setAttribute('from', '400');
    growAnimation.setAttribute('to', '10');
    growAnimation.setAttribute('dur', '3s');
    growAnimation.setAttribute('fill', 'freeze');
    growAnimation.setAttribute('begin', 'indefinite');
    groupRect.appendChild(growAnimation);

    growAnimation.beginElement();
  }, 1000);
})();