SVG animation guidance needed - animation

I read this Pocket Guide to SVG today and learned a lot from it, but... it does not cover animation. I need some guidance on how to do a simple animation. I dont need any transitions between frames, I just want to define shapes for frame one, shapes for frame two, etc.
https://svgpocketguide.com/book/
<!-- https://svgpocketguide.com/book/#section-4 -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="1080" height="1080">
<!-- the doctype and svg need to be like this to render in Firefox -->
<rect width="1080" height="1080" fill="white"/>
<!-- delay frames by waiting 0.5 seconds between frames -->
<animation frame 1>
<line x1="75" y1="75" x2="10" y2="50" stroke="hsl(0,82%,56%)" stroke-width="3"/>
<line x1="75" y1="75" x2="175" y2="475" stroke="hsl(236,82%,56%)" stroke-width="3"/>
<circle cx="10" cy="50" r="5" fill="black" />
<circle cx="75" cy="75" r="5" fill="black" />
<circle cx="175" cy="475" r="5" fill="black" />
</animation frame 1>
<animation frame 2>
<line x1="75" y1="75" x2="10" y2="50" stroke="hsl(0,82%,56%)" stroke-width="3"/>
<line x1="75" y1="75" x2="175" y2="475" stroke="hsl(236,82%,56%)" stroke-width="3"/>
<circle cx="10" cy="50" r="5" fill="black" />
<circle cx="75" cy="75" r="5" fill="black" />
<circle cx="175" cy="475" r="5" fill="black" />
</animation frame 2>
</svg>

<!-- https://svgpocketguide.com/book/#section-4 -->
<!-- https://oak.is/thinking/animated-svgs/ -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="1080" height="1080">
<rect width="1080" height="1080" fill="white"/>
<line x1="400" y1="400" x2="10" y2="515" stroke="hsl(250,82%,56%)" stroke-width="3">
<animate
attributeName="x1"
dur="0.5s"
values="50;100;200;350;400"
calcMode="discrete"
repeatCount="1"
/>
<animate
attributeName="y1"
dur="0.5s"
values="50;100;200;350;400"
calcMode="discrete"
repeatCount="1"
/>
<animate
attributeName="stroke"
dur="0.5s"
values="hsl(50,82%,56%);hsl(100,82%,56%);hsl(150,82%,56%);hsl(200,82%,56%);hsl(250,82%,56%)"
calcMode="discrete"
repeatCount="1"
/>
</line>
<circle cx="50" cy="50" r="5" fill="black" />
<circle cx="100" cy="100" r="5" fill="black" />
<circle cx="200" cy="200" r="5" fill="black" />
<circle cx="350" cy="350" r="5" fill="black" />
<circle cx="400" cy="400" r="5" fill="black" />
</svg>

This question shows a method for frame-based animation in an SVG. It uses CSS animation, but you could achieve the same thing with SMIL. However, the CSS way is a little cleaner IMO.
SIMPLE – Animate Multiple SVGs in Sequence (like a looping GIF)
Here's another answer, that may help: SVG frame-based animation

Related

Hard svg animation

I need your help with the implementation of svg animation.
https://youtu.be/lrWjkARl8Zg
(sorry for the poor video quality)
And I have such a structure of svg
I need the arrow (class = "arrow") itself to move along long lines and draw it.
<svg class="vector" width="1193" height="329" viewBox="0 0 1193 329" fill="none" xmlns="http://www.w3.org/2000/svg">
<path class="long_line" d="M1436 327.98L84.7148 327.98C73.8082 328.219 62.9639 326.275 52.818 322.262C42.6722 318.249 33.4293 312.249 25.6315 304.613C17.8337 296.977 11.6384 287.858 7.40866 277.793C3.17891 267.728 0.999997 256.919 0.999996 246C0.999996 235.081 3.17891 224.272 7.40866 214.207C11.6384 204.142 17.8337 195.023 25.6315 187.387C33.4292 179.751 42.6722 173.75 52.818 169.738C62.9638 165.725 73.8082 163.781 84.7147 164.02L589.173 164.02" stroke="black" stroke-width="2" stroke-miterlimit="10"/>
<path class="arrow" d="M544 204L589 164L548 124" stroke="black" stroke-width="2"/>
<path class="arrow" d="M504 1L459 41L500 81" stroke="black" stroke-width="2"/>
<path class="short_line" d="M1308 41L459 41" stroke="black" stroke-width="2" stroke-miterlimit="10"/>
</svg>
Advise which library I can use
You could do this using svg smil animations:
A Guide to SVG Animations (SMIL)
Your arrow animations could be achieved by
<animateMotion> for moving the arrow element and
animating the stroke-dashoffset property.
Animated example
svg {
border: 1px solid #ccc;
width: 33%;
overflow: visible;
display: block;
}
path {
stroke-width: 10;
stroke: #000;
}
<p>Click on animation for replay</p>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1400 500">
<defs>
<path id="arrow" fill="none" d="M-45,40L0,0l-41-40" />
</defs>
<use id="arrow1" href="#arrow" />
<use id="arrow2" href="#arrow" />
<g id="graphics">
<rect id="graphics" fill="#fff" fill-opacity="0" x="0" y="0" width="100%" height="100%" />
<path id="mpath-long" fill="none" pathLength="100" stroke-width="2" stroke-miterlimit="10" stroke-dashoffset="100" stroke-dasharray="100" d="M1571,413H219.7
c-10.9,0.2-21.8-1.7-31.9-5.7c-10.1-4-19.4-10-27.2-17.6c-7.8-7.6-14-16.8-18.2-26.8S136,341.9,136,331s2.2-21.7,6.4-31.8
s10.4-19.2,18.2-26.8c7.8-7.6,17-13.6,27.2-17.6c10.1-4,21-6,31.9-5.7h504.5" />
<path id="mpath-short" fill="none" pathLength="100" stroke-width="2" stroke-miterlimit="10" stroke-dashoffset="100" stroke-dasharray="100" d="M1443,126H594" />
</g>
<animateMotion href="#arrow1" dur="2" rotate="auto" repeatCount="1" begin="0;graphics.click" fill="freeze">
<mpath href="#mpath-long" />
</animateMotion>
<animate attributeType="XML" href="#mpath-long" id="strokeAni" attributeName="stroke-dashoffset" from="100" to="0" dur="2s" repeatCount="1" begin="0;graphics.click" fill="freeze" />
<animateMotion href="#arrow2" dur="2s" rotate="auto" repeatCount="1" begin="0;graphics.click" fill="freeze">
<mpath href="#mpath-short" />
</animateMotion>
<animate attributeType="XML" href="#mpath-short" attributeName="stroke-dashoffset" from="100" to="0" dur="2s" repeatCount="1" fill="freeze" begin="0;graphics.click" />
</svg>
Quite likely, you will have to tweak your svg viewBox to get the desired result.
A common trick is to position element that's supposed to move along the motion path to x/y = 0.
Static example
<style>
svg{
border: 1px solid #ccc;
width: 33%;
overflow:visible;
display:block;
}
path{
stroke-width:10;
stroke: #000;
}
</style>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1400 500">
<defs>
<path id="arrow" fill="none" d="M-45,40L0,0l-41-40" />
</defs>
<use id="arrow1" href="#arrow" />
<use id="arrow2" href="#arrow" />
<g id="graphics">
<rect id="graphics" fill="#fff" fill-opacity="0" x="0" y="0" width="100%" height="100%" />
<path id="mpath-long" fill="none" pathLength="100" stroke-width="2" stroke-miterlimit="10" stroke-dashoffset="0" stroke-dasharray="100" d="M1571,413H219.7
c-10.9,0.2-21.8-1.7-31.9-5.7c-10.1-4-19.4-10-27.2-17.6c-7.8-7.6-14-16.8-18.2-26.8S136,341.9,136,331s2.2-21.7,6.4-31.8
s10.4-19.2,18.2-26.8c7.8-7.6,17-13.6,27.2-17.6c10.1-4,21-6,31.9-5.7h504.5" />
<path id="mpath-short" fill="none" pathLength="100" stroke-width="2" stroke-miterlimit="10" stroke-dashoffset="0" stroke-dasharray="100" d="M1443,126H594" />
</g>
</svg>

How to reverse animate motion?

Why my animation doesn't start from the top to the bottom ? How to reverse it correctly ? I don't know what I should change. (I tried keyPoints="1;0" keyTimes="0;1" which didn't work)
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="350" viewBox="0 0 500 350"><path d="M404.88 470.22V79.69c-.2-19-14.21-33-17.52-36.19s-16.77-15.19-34.7-15.45h-1.11c-28.65.35-59.55-.12-319.52 0H28" stroke="#000" stroke-miterlimit="10" fill="none" id="motionPath"></path><rect id="circle" x="-25" y="-25" rx="15" ry="15" width="50" height="50"></rect><animateMotion xlink:href="#circle" from="50" to="450" dur="5s" begin="0s" repeatCount="1" rotate="auto" fill="freeze"><mpath xlink:href="#motionPath"></mpath></animateMotion></svg>
It is necessary to remove from =" 50 " to =" 450 " since the length of the path of movement is determined by the length of the path mpath
The direction of movement of an object along a path depends on two parameters
keyPoints="1;0" - movement from start to finish
keyTimes="0;1"
keyPoints="0;1" - movement from end to start
keyTimes="0;1"
In the example below, JS is used only to handle the event of pressing the control buttons: forward and back
var animation1 = document.getElementById("forward")
function forwardSVG(){
animation1.beginElement();
}
var animation2 = document.getElementById("back")
function backSVG(){
animation2.beginElement();
}
<div id="pathContainer4">
<button id="btn1" onclick="forwardSVG()">forward</button />
<button id="btn2" onclick="backSVG()">Back</button />
</div>
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="350" viewBox="50 0 500 350">
<path id="motionPath" d="M404.88 470.22V79.69c-.2-19-14.21-33-17.52-36.19s-16.77-15.19-34.7-15.45h-1.11c-28.65.35-59.55-.12-319.52 0H28" stroke="#000" stroke-miterlimit="10" fill="none" >
</path>
<rect id="circle" x="0" y="-25" rx="15" ry="15" width="50" height="50"></rect>
<!-- Forward motion animation -->
<animateMotion id="forward"
xlink:href="#circle"
dur="5s"
begin="indefinite"
rotate="auto"
fill="freeze"
repeatCount="1"
keyPoints="1;0"
keyTimes="0;1"
calcMode="linear">
<mpath xlink:href="#motionPath"></mpath>
</animateMotion>
<!-- Backward motion animation -->
<animateMotion id="back"
xlink:href="#circle"
dur="5s"
begin="indefinite"
rotate="auto"
fill="freeze"
repeatCount="1"
keyPoints="0;1"
keyTimes="0;1"
calcMode="linear">
<mpath xlink:href="#motionPath"></mpath>
</animateMotion>
</svg>
Here is the reverse you want.
I modified the shape a little but you can modify it back again.
Hope that this is the solution to your problem.
you can make it freeze too if you want.
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="350" viewBox="0 0 500 350">
<g>
<path stroke="#000" stroke-miterlimit="10" fill="none" id="motionPath"
d="M404.88 470.22V79.69c-.2-19-14.21-33-17.52-36.19s-16.77-15.19-34.7-15.45h-1.11c-28.65.35-59.55-.12-319.52 0H28"/>
<rect id="circle" x="-25" y="-25" rx="15" ry="15" width="50" height="60" fill="black"
stroke="black" stroke-width="1" transform="translate(-25,-10)">
<animateMotion path="M404.88 470.22V79.69c-.2-19-14.21-33-17.52-36.19s-16.77-15.19-34.7-15.45h-1.11c-28.65.35-59.55-.12-319.52 0H28"
begin= "0s" dur="5s" repeatCount="1" rotate="auto" fill="freeze"
keyPoints="1;0" keyTimes="0;1" calcMode="linear"/>
</rect>
</g>
</svg>

Animating a defined path's d attribute doesn't work on firefox

I'm animating a d attribute of a path that is wrapped around a defs tag and linked using a
few use tags. This works fine on Chrome, however no animation on Firefox. I tried this with relative and absolute paths to no avail.
<svg viewBox="0 0 300 100">
<defs>
<path id="a" d="M0,20 H200 V70 H0z" />
</defs>
<use xlink:href="#a" />
<animate xlink:href="#a" attributeName="d" values="M0,20 H200 V70 H0z; M0,20 H200 V45 H0z" keyTimes="0;1" dur="1s" begin="0s" fill="freeze" />
</svg>
Is the only way to make this work is by repeating the path and animating them all or is there a way to make this work on Firefox?
As commented by #Robert Longson
I think you'd need to repeat the paths. At the moment SMIL changes to
the things that a element points to do not trigger the to
re-render.
Therefore, it is necessary to transfer the animation directly inside the <path> tags
<svg viewBox="0 0 300 100">
<defs>
<path id="a" d="M0,20 H200 V70 H0z" >
<animate
attributeName="d"
values="
M0,20 H200 V70 H0z;
M0,20 H200 V45 H0z"
keyTimes="0;1"
dur="1s"
begin="0s"
fill="freeze" />
</path>
</defs>
<use xlink:href="#a" />
</svg>
Start animation after click
<svg id ="svg1" viewBox="0 0 300 100">
<defs>
<path id="a" d="M0,20 H200 V70 H0z" >
<animate
attributeName="d"
values="
M0,20 H200 V70 H0z;
M0,20 H200 V45 H0z"
keyTimes="0;1"
dur="1s"
begin="svg1.click"
fill="freeze" />
</path>
</defs>
<use xlink:href="#a" />
</svg>

Easing animateMotion in SVG

I want to apply an easing to an animateMotion tag.
I'm confused about which attributes are relevant to animate a shape: if I understood well calcMode="spline" is required, along with the definition of keyTimes and keySplines; but what about the use of keySplines and values?
However, I tried to insert timings into my animation, but something went wrong:
<g style="transform-origin:50%;transform: rotate(180deg);">
<path id="verticalMotionPath" d="m 100,100 0, 50" stroke-width="5px" stroke="red" fill="none" stroke-linecap="round" stroke-linejoin="round" />
<circle cx="0" cy="0" r="5" fill="#333333">
<animateMotion dur="0.2s" repeatCount="once" fill="freeze" calcMode="spline" keyPoints="0.25;0.50;0.75;1" keyTimes="0;0.25;0.75;1">
<mpath xlink:href="#verticalMotionPath"/>
</animateMotion>
</circle>
</g>
My aim would be to apply to this example timings to draw from tools like this one
You are specifying calcMode="spline", but you haven't provided a keySplines attribute.
The value for keySplines can just be copied from your spline editing tool.
<circle cx="0" cy="0" r="5" fill="#333333">
<animateMotion dur="2.2s" repeatCount="once" fill="freeze"
calcMode="spline" keyTimes="0;1" keySplines="0.1 0.8 0.9 0.1">
<mpath xlink:href="#verticalMotionPath"/>
</animateMotion>
</circle>
Full demo (I've slowed down the animation so you can see it is working).
<!DOCTYPE HTML>
<html>
<body>
<?xml version="1.0"?>
<svg width="400" height="400" viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink" style="background:aquamarine">
<style>
path {
animation-name:animateDash;
animation-duration:5s;
animation-iteration-count:once;
animation-fill-mode:forwards;
}
#keyframes animateDash {
from{stroke-dasharray:0,2305}
to {stroke-dasharray:2305,0}
}
</style>
<g style="transform-origin:50%;transform: rotate(180deg);">
<path id="verticalMotionPath" d="m 100,100 0, 50" stroke-width="5px" stroke="red" fill="none" stroke-linecap="round" stroke-linejoin="round" />
<circle cx="0" cy="0" r="5" fill="#333333">
<animateMotion dur="2.2s" repeatCount="once" fill="freeze"
calcMode="spline" keyTimes="0;1" keySplines="0.1 0.8 0.9 0.1">
<mpath xlink:href="#verticalMotionPath"/>
</animateMotion>
</circle>
</g>
<g style="transform-origin:50%;transform: rotate(60deg);">
<path id="verticalMotionPath" d="m 100,100 0, 50" stroke-width="5px" stroke="white" fill="none" stroke-linecap="round" stroke-linejoin="round" />
<circle cx="0" cy="0" r="5" fill="#333333">
<animateMotion dur="0.2s" repeatCount="once" fill="freeze">
<mpath xlink:href="#verticalMotionPath"/>
</animateMotion>
</circle>
</g>
<g style="transform-origin:50%;transform: rotate(-60deg);">
<path id="verticalMotionPath" d="m 100,100 0, 50" stroke-width="5px" stroke="blue" fill="none" stroke-linecap="round" stroke-linejoin="round" />
<circle cx="0" cy="0" r="5" fill="#333333">
<animateMotion dur="0.2s" repeatCount="once" fill="freeze">
<mpath xlink:href="#verticalMotionPath"/>
</animateMotion>
</circle>
</g>
</svg>
</body>
</html>
This is how I would do it although I don't use animateMotion.
Since your path is made up of lines you can define the values for <animateTransform> like this:
Your path's d="M100, 100 L100, 47 146, 73"
the animation's values="100, 100; 100, 47;146, 73"
This is a working example:
<svg width="400" height="400" viewBox="0 0 200 200" style="background:aquamarine">
<path id="theMotionPath" d="M100, 100 L100, 47 146, 73" stroke-width="5px" stroke="antiquewhite" fill="none" stroke-linecap="round" stroke-linejoin="round" />
<circle r="5" >
<animateTransform
attributeType="XML"
attributeName="transform"
type="translate"
values="100,100; 100,47; 146,73"
keySplines= ".5 0 .5 1; 0 .75 .25 1";
calcMode="spline"
dur="4s"
repeatCount="indefinite" />
</circle>
</svg>
The keySplines length must be equal to the length of the values - 1. In this case values.length = 3 thus keySplines.length = 2, i.e: the movement from the first to the second value is defined by the first key spline; the movement from the second to the third value is defined by the second key spline. Every value of the keySplines is defining the 2 control points of a Bézier curve.

Why does the SVG animation stop when using xlink:href for external file

For some reason my spinner.svg is not animating when using xlink:href. Embedding the SVG on the page and using xlink:href seems to work fine, as the snippet below shows.
The problem: static (and solid!) spinner instead of animation
Why are the animation tags of the SVG suddenly not taking effect? The reference must be working since the image is actually displaying.
EDIT: Could this have to do with the shadow dom and xref?
According to Sara Soueidan "The target element must be part of the current SVG document fragment". I might be overloading what "document fragment" means, but to me document fragments belong in Shadow DOM land, and I suspect that SMIL animations might not work when using <use> statements due to this?
Working versions
.xlinked {
color: green;
}
img {
color: red; // not taking effect - of course! just a test.
}
.embedded {
color: blue;
}
<h1>xlink local</h1>
<svg class="xlinked">
<!-- could not get this external reference to work?
<use xlink:href="http://imgh.us/spinner_1.svg" />
-->
<use xlink:href="#spinner" />
</svg>
<h1>embedded</h1>
<div class="embedded">
<svg id="embedded" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
<circle cx="50" cy="50" r="45" stroke="rgba(43,43,43,0.3)" fill="none" stroke-width="10" stroke-linecap="round"/>
<circle cx="50" cy="50" r="45" stroke="currentColor" fill="none" stroke-width="6" stroke-linecap="round">
<animate attributeName="stroke-dashoffset" dur="2s" repeatCount="indefinite" from="0" to="502"/>
<animate attributeName="stroke-dasharray" dur="2s" repeatCount="indefinite" values="150.6 100.4;1 250;150.6 100.4"/>
</circle>
</svg>
</div>
<h1>img</h1>
<img src="http://imgh.us/spinner_1.svg" />
<h1>External absolute xlink (not working)</h1>
<svg>
<!-- could not get this external reference to work. should be the same as a local reference? -->
<use xlink:href="http://imgh.us/spinner_1.svg" />
</svg>
<h1>Source SVG with symbols for xlink reference </h1>
<svg xmlns="http://www.w3.org/2000/svg"><symbol id="spinner" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="50" cy="50" r="45" stroke="rgba(43,43,43,0.3)" fill="none" stroke-width="10" stroke-linecap="round"/><circle cx="50" cy="50" r="45" stroke="currentColor" fill="none" stroke-width="6" stroke-linecap="round"><animate attributeName="stroke-dashoffset" dur="2s" repeatCount="indefinite" from="0" to="502"/><animate attributeName="stroke-dasharray" dur="2s" repeatCount="indefinite" values="150.6 100.4;1 250;150.6 100.4"/></circle></symbol></svg>
In SVG1.1, Use element requires reference to SVG fragment not SVG URL. Try to add id attribute to root svg element of external SVG file and add hash string to href value of use element, like this.
external svg(spinner_1.svg)
<svg xmlns="http://www.w3.org/2000/svg" id="root">
<!--svg structure-->
</svg>
html uses external SVG file
<svg>
<use xlink:href="http://imgh.us/spinner_1.svg#root" />
</svg>
Note:In SVG2, you can set SVG URL to href attribute of use element. See https://www.w3.org/TR/2016/CR-SVG2-20160915/struct.html#UseElement

Resources