I have a relatively simple path animated via SMIL. Everything works as expected in Chrome and Firefox. IE/Edge support is irrelevant.
Safari, however, behaves strangely and is easy to recreate. Unfortunately, it is crucial that Safari works.
<nav>
<button id="button1" onClick="document.getElementById('animate1').beginElement()">1</button>
<button id="button2" onClick="document.getElementById('animate2').beginElement()">2</button>
<button id="button3" onClick="document.getElementById('animate3').beginElement()">3</button>
<button id="button4" onClick="document.getElementById('animate4').beginElement()">4</button>
<button id="button5" onClick="document.getElementById('animate5').beginElement()">5</button>
</nav>
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
width="100%"
height="100%"
viewBox="0 0 1000 1000"
style="enable-background:new 0 0 1000 1000;"
xml:space="preserve"
preserveAspectRatio="none">
<path d="M0,0 830,0 1000,300 1000,1000 500,1000 80,1000z">
<animate id="animate1" attributeName="d" begin="indefinite" dur=".3s" fill="freeze" keyTimes="0;1" to="M0,0 830,0 1000,300 1000,1000 500,1000 80,1000z" calcMode="spline" keySplines="0.4, 0, 0.2, 1" />
<animate id="animate2" attributeName="d" begin="indefinite" dur=".3s" fill="freeze" keyTimes="0;1" to="M150,0 750,0 1000,150 1000,1000 500,1000 0,1000z" calcMode="spline" keySplines="0.4, 0, 0.2, 1" />
<animate id="animate3" attributeName="d" begin="indefinite" dur=".3s" fill="freeze" keyTimes="0;1" to="M50,0 750,0 1000,150 1000,1000 500,1000 0,1000z" calcMode="spline" keySplines="0.4, 0, 0.2, 1" />
<animate id="animate4" attributeName="d" begin="indefinite" dur=".3s" fill="freeze" keyTimes="0;1" to="M250,0 750,0 1000,450 1000,1000 500,1000 0,1000z" calcMode="spline" keySplines="0.4, 0, 0.2, 1" />
<animate id="animate5" attributeName="d" begin="indefinite" dur=".3s" fill="freeze" keyTimes="0;1" to="M0,0 930,0 1000,600 1000,1000 500,1000 80,1000z" calcMode="spline" keySplines="0.4, 0, 0.2, 1" />
</path>
</svg>
Codepen: https://codepen.io/anon/pen/vwWKYN
Upon click of any button, the path animates. Additional button clicks will properly animate the svg until the first button selected is clicked a second time.
E.g. Click on 4. 1,2,3,5 will work until you click 4 again. Once you click 4 a second time, the animation stops and you must select 1,2,3,5 in any order to 'unlock' the animation. Clicking any button other than the 4, they will continue to animate.
I've spent several hours on this and have attempted adding 'from' in addition to 'to', changed paths, order, keySplines, keyTimes. I've also created a js function with the same result.
Note: I am trying to avoid the use of a third party library. I realize SMIL isn't as good as GSAP or Snap.js but this is all I need to animate and it mostly works.
Thanks for any help!
Related
I have the following SVG I animate with SMIL - this works fine on click, but will be rerun on repeating clicks - how can I prevent this? I want it to only run once and then do nothing on another click!
<g id="Gruppe_703" data-name="Gruppe 703" transform="translate(0 -185)" opacity="0">
<animateTransform
attributeName="transform"
attributeType="XML"
type="translate"
from="0 -185"
to="0 -39"
dur="0.1s"
begin="Gruppe_589.click"
fill="freeze"/>
</g>
Set the pointer-events property to none at the end of the animation then further mouse clicks are ignored.
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
<polygon points="60,60 90,120 30,120">
<animateTransform
id="at"
attributeName="transform"
type="translate"
from="0 0"
to="0 -39"
dur="1s"
begin="click"
fill="freeze"/>
<set
attributeName="pointer-events"
to="none"
begin="at.end"/>
</polygon>
</svg>
Consider other options to prevent re-animation after a click:
restart = "whenNotActive"
This value indicates that the animation can only be restarted when it
is not active (i.e. after the active end). Attempts to restart the
animation during its active duration are ignored.
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
<polygon points="60,60 90,120 30,120">
<animateTransform
id="at"
attributeName="transform"
type="translate"
from="0 0"
to="0 -39"
dur="3s"
begin="click"
fill="freeze"
restart="whenNotActive"/>
</polygon>
</svg>
restart = "never"
This value indicates that the animation cannot be restarted for the
time the document is loaded.
In other words, the animation fires only once, it cannot be restarted.
The animation can only be started again after reloading the document.
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
<polygon points="60,60 90,120 30,120">
<animateTransform
id="at"
attributeName="transform"
type="translate"
from="0 0"
to="0 -39"
dur="3s"
begin="click"
fill="freeze"
restart="never"/>
</polygon>
</svg>
I have made the transparent text appear and disappear within 3 seconds, and I want to click the SVG to make the disappeared text appear again and again, is it poosible to make it?
Thanks for any help and creative thougths
<svg>
<text x="0" y="50" font-family="Verdana" font-size="35" fill="blue" opacity="0">Hello
<animate attributeName="opacity" begin="click" dur="0.3s" from="0" to="1" restart="never" fill="freeze"></animate>
<animate attributeName="opacity" begin="click+3" dur="0.3s" from="1" to="0" restart="never" fill="freeze">
</animate>
</text>
</svg>
Don't use 2 animations
For the begin use a list of values separated with semicolons: begin="theSVG.click;theSVG.click+3"
Use repeatCount="indefinite" for an infinite animation.
Instead of using the from and to attributes you can use a values attribute values="0;1;0" where the values are separates by semicolons.
Also the user can't know where the text is so clicking the text may be a problem. Instead you cah give the svg element an id (theSVG - in this case) and use this id to start the animation when the user is clicking the svg element begin="theSVG.click;theSVG.click+3"
You can use any other visible svg element for this.
svg{border:solid}
<svg id="theSVG">
<text x="0" y="50" font-family="Verdana" font-size="35" fill="blue" opacity="0">Hello
<animate attributeName="opacity" begin="theSVG.click;theSVG.click+3" dur="0.3s" values="0;1;0" restart="never" repeatCount="indefinite"></animate>
</text>
</svg>
UPDATE
The OP is commenting:
I am tring to make the "HELLO" re-clickable after it disappear after 3s, what should I do?
In this case you need to delete restart="never"
svg{border:solid}
<svg id="theSVG">
<text x="0" y="50" font-family="Verdana" font-size="35" fill="blue" opacity="0">Hello
<animate attributeName="opacity" begin="theSVG.click" dur="3s" values="0;1;0" repeatCount="1" fill="freeze"></animate>
</text>
</svg>
I have an SVG animation where I move an object alongside a path. At some point I am changing the motion path.
While in Firefox the animated object follows the new path, in Chrome it continue to move on the old one. Does anyone knows why it happens and how can this be fixed?
Here is an example:
function change(){
elem = document.getElementById("Zuerich_Geneva");
elem.setAttribute('d','M382500,53500 C632500,53500 549500,80000 499500,181000')
}
setTimeout(function() { change(); }, 5000);
<svg xml:space="preserve" viewBox="480000 0 360000 240000" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="airplane" overflow="visible">
<path stroke="blue" stroke-width="100" fill="lightgray" d="M-4000,0 a1000,300 0 0,1 1000,-300 H-1000 L1500,-3000 h400 L0,-300 h2000 L3000,-1500 h500 L2500,-50 V100 L3500,1500 h-500 L2000,300 h-2000 L1900,3000 h-400 L-1000,300 H-3000 a1000,300 0 0,1 -1000,-300"/>
</symbol>
</defs>
<g id="AnimationPaths">
<path id="Zuerich_Geneva" stroke="orange" stroke-width="2000" fill="none" d="M682500,53500 C632500,53500 549500,80000 499500,181000"/>
<use id="AirplaneZurichGeneva" xlink:href="#airplane">
<animateMotion id="animMotionZurGen" dur="10s" repeatCount="indefinite" rotate="auto-reverse" keyPoints="1;0" keyTimes="0;1" calcMode="linear">
<mpath xlink:href="#Zuerich_Geneva"/>
</animateMotion>
</use>
</g>
</svg>
Thanks!
According to the SVG 1.1 spec, <mpath> is not animatable.
https://www.w3.org/TR/SVG/animate.html#MPathElementHrefAttribute
However, you are not actuall animating the <mpath>, you are animating the <path> that it is referencing. So I think it should work. It seems like a bug in Chrome, which you should report. However since Chrome have deprecated SMIL, they may not be interested in fixing it.
There are workarounds. For instance, you can define more than one <animateMotion> and switch between them. In the example below, I have programmed the switch in the animation. But you could also do it with JS.
<svg xml:space="preserve" viewBox="480000 0 360000 240000" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="airplane" overflow="visible">
<path stroke="blue" stroke-width="100" fill="lightgray" d="M-4000,0 a1000,300 0 0,1 1000,-300 H-1000 L1500,-3000 h400 L0,-300 h2000 L3000,-1500 h500 L2500,-50 V100 L3500,1500 h-500 L2000,300 h-2000 L1900,3000 h-400 L-1000,300 H-3000 a1000,300 0 0,1 -1000,-300"/>
</symbol>
</defs>
<g id="AnimationPaths">
<path id="Zuerich_Geneva" stroke="orange" stroke-width="2000" fill="none" d="M682500,53500 C632500,53500 549500,80000 499500,181000"/>
<path id="Zuerich_Geneva2" stroke="orange" stroke-width="2000" fill="none" d="M382500,53500 C632500,53500 549500,80000 499500,181000"/>
<use id="AirplaneZurichGeneva" xlink:href="#airplane">
<animateMotion id="animMotionZurGen" dur="5s" repeatCount="indefinite"
rotate="auto-reverse" keyPoints="1;0.5" keyTimes="0;1"
calcMode="linear" begin="0s; animMotionZurGen2.begin + 5s">
<mpath xlink:href="#Zuerich_Geneva"/>
</animateMotion>
<animateMotion id="animMotionZurGen2" dur="5s" repeatCount="indefinite"
rotate="auto-reverse" keyPoints="0.5;0" keyTimes="0;1"
calcMode="linear" begin="animMotionZurGen.begin + 5s">
<mpath xlink:href="#Zuerich_Geneva2"/>
</animateMotion>
</use>
</g>
</svg>
I have a 17 second long second animation with multiple parts to it. How can I repeat the whole sequence once it's done?
I tried repeatCount = "indefinite" attribute on each <animate>, but that just made things weird. Here's the animation I want repeated, for reference or something.
<rect x="185" y="300" width="300" height="400" fill="#666666">
<animate
attributeName="x"
from="185" to="145"
begin="5s"
dur="2s"
fill="freeze"
/>
<animate
attributeName="x"
from="145" to="185"
begin="9s"
dur="2s"
fill="freeze"
/>
<animate
attributeName="y"
from="300" to="340"
begin="11s"
dur="2s"
fill="freeze"
/>
<animate
attributeName="y"
from="340" to="300"
begin="15s"
dur="2s"
fill="freeze"
/>
</rect>
In addition to animation begin values being clock-based time intervals, they can also be sync-based values that reference other animations.
From MDN Web Docs:
<syncbase-value>Describes a syncbase and an optional offset from that syncbase. The element's animation start time is defined relative to the begin or active end of another animation. A syncbase consists of an ID reference to another animation element followed by either .begin or .end to identify whether to synchronize with the beginning or active end of the referenced animation element.
This means you can chain animations together by referencing each others' IDs as begin values.
Basically, you'll want to start each animation after the previous one ends. When the last animation ends it should trigger the first to start again. The animation begin attribute accepts multiple values so you can specify two values for the first animation: the 5s delay and the end of the last animation.
<svg viewBox="0 0 400 500" width="50%" height="50%">
<rect x="50" y="10" width="300" height="400" fill="#666666">
<animate
id="anim1"
attributeName="x"
from="50" to="10"
begin="5s;anim4.end"
dur="2s"
fill="freeze"
/>
<animate
id="anim2"
attributeName="x"
from="10" to="50"
begin="anim1.end"
dur="2s"
fill="freeze"
/>
<animate
id="anim3"
attributeName="y"
from="10" to="50"
begin="anim2.end"
dur="2s"
fill="freeze"
/>
<animate
id="anim4"
attributeName="y"
from="50" to="10"
begin="anim3.end"
dur="2s"
fill="freeze"
/>
</rect>
</svg>
You can try with this:
<animate attributeName="x" values="185; 145; 185" begin="5s" dur="4s" fill="freeze"/>
<animate attributeName="y" values="300; 340; 300" begin="11s" dur="4s" fill="freeze"/>
I have a SVG graphic that I am animating via animateTransform. It animates in, then stays on screen until you click it, then it scales down to 0 and cannot be brought back. What I want to do is restart the entire animation sequence over say 2 seconds after the last animation has ended (scaled to 0), so it will animate back in.
How can I do this? Thanks!
<!-- Keep scaled at 0 on load -->
<animateTransform
attributeName="transform"
attributeType="XML"
type="scale"
from="0"
to="0"
dur="0.5s"/>
<!-- Scale up after 0.5 seconds -->
<animateTransform
attributeName="transform"
attributeType="XML"
type="scale"
from="0"
to="1"
begin="0.5s"
dur="0.3s"
fill="freeze"
additive="sum"/>
<!-- Scale down on click -->
<animateTransform id="animatFinished"
attributeName="transform"
attributeType="XML"
type="scale"
from="1"
to="0"
begin="click"
dur="0.6s"
fill="freeze"
additive="sum"/>
We need to start the first transform both at time 0 and 2 seconds after the onclick animation has ended which gives us begin="0s;animatFinished.end+2s"
The second animation needs to start when the first has ended otherwise it will only fire once.
And the use of additive="sum" is giving you problems as the animations end up trying to add a scale to something which has a scale of 0 which does nothing as 0 x value = 0.
So, I think this is what you're looking for:
<!-- Keep scaled at 0 on load -->
<animateTransform id="one"
attributeName="transform"
attributeType="XML"
type="scale"
from="0"
to="0"
begin="0s;animatFinished.end+2s"
dur="0.5s"
fill="freeze"/>
<!-- Scale up after 0.5 seconds -->
<animateTransform
attributeName="transform"
attributeType="XML"
type="scale"
from="0"
to="1"
begin="one.end"
dur="0.3s"
fill="freeze"/>
<!-- Scale down on click -->
<animateTransform id="animatFinished"
attributeName="transform"
attributeType="XML"
type="scale"
from="1"
to="0"
begin="click"
dur="0.6s"
fill="freeze"/>
get the animateTransform element(s) you want to restart and use beginElement() method
const animateEl = document.querySelector("#animatFinished");
animateEl.beginElement();