问题
I have a transition animation I want to trigger on the width of an SVG element inside a group element. The animation only seems to work on the Chrome browser.
<!-- 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);
})();
To reproduce open this fiddle in Safari or Firefox. You will see that the transition of the second rectangle does not work properly.
Is there any workaround to get the transition animation working for a specific element inside the SVG group?
回答1:
use:
The element takes nodes from within the SVG document, and duplicates them somewhere else. - mdn
So It is unncesssery to use use
since you do not duplicate it.
(() => {
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>
Update 1
Even SVG <animate>
does not work in FireFox when the element is duplicated. Though if not used inside the refrensed element. This is a known issue. There are multiple unanswered question on stackoverflow, for instance 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>
Update 2
Animations on a referenced element will cause the instances to also be animated. use - w3
Per documentation, if you animate the refrenced element (`myCircle), all the duplicate of it should be aniamted too. So I guess, it is a bug that it does not work in Firefox & Safari.
Update 3 - workaround
Use svg <animate>
tag and enclose it in <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>
回答2:
How about nesting the animate tag inside circle?
<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>
回答3:
Summarising, a workaround is to fall back to SMIL SVG animations instead of using CSS.
See the adapted fiddle to my original question.
<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);
})();
来源:https://stackoverflow.com/questions/60078402/animation-on-svg-use-does-not-work-in-firefox-safari