SVG animate a bezier curve to grow by following an arrowhead - animation
I'm trying to create an arrow with its arrowhead moving from a starting point and ending at its target. Moving the arrowhead was achieved successfully with the tip from #Robert Longson. I want the end of the shaft also to follow the arrowhead and grow to its full length. The code is given below and notice that the shaft isn't growing with the arrowhead and also ends up in partial length. Is there a way to correct this. Any help will be greatly appreciated.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="5 0 100 45">
<style>
.wire {
fill: none;
stroke: red;
stroke-width: 1px;
/* Stroke-dasharray property to animate */
stroke-dasharray: 100%;
stroke-dashoffset: 100%;
animation: move linear 5s;
}
#keyframes move {
100% {
stroke-dashoffset: 0;
}
}
</style>
<path d="M10,10 C15,50 95,50 100,10" stroke="blue" stroke-width="2" id="wire" class="wire">
<animate>
<mpath xlink:href="#wire" />
</animate>
</path>
<!-- acceptable movement along the path but incorrect orientation -->
<polygon points="-5,-5 5,0 -5,5 -3,0" fill="red">
<animateMotion dur="5s" repeatCount="1" rotate="auto" fill="freeze">
<mpath xlink:href="#wire" />
</animateMotion>
</polygon>
</svg>
Like this I guess. Note that you could have done this in SMIL too.
I've set the animation to be forwards so it remains at the end, otherwise the curve disappears at the end of the animation.
The console.log line shows you where I got the number from.
console.log(document.getElementsByTagName("path")[0].getTotalLength())
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="5 0 100 45">
<style>
.wire {
fill: none;
stroke: red;
stroke-width: 1px;
/* Stroke-dasharray property to animate */
stroke-dasharray: 118.27912902832031;
stroke-dashoffset: 118.27912902832031;
animation: move linear 5s forwards;
}
#keyframes move {
100% {
stroke-dashoffset: 0;
}
}
</style>
<path d="M10,10 C15,50 95,50 100,10" stroke="blue" stroke-width="2" id="wire" class="wire">
<animate>
<mpath xlink:href="#wire" />
</animate>
</path>
<!-- acceptable movement along the path but incorrect orientation -->
<polygon points="-5,-5 5,0 -5,5 -3,0" fill="red">
<animateMotion dur="5s" repeatCount="1" rotate="auto" fill="freeze">
<mpath xlink:href="#wire" />
</animateMotion>
</polygon>
</svg>
To move the cursor and line at the same time:
It is necessary to synchronize both animations in start time begin="svg1.click" and duration dur="5s"
The maximum curve length is 118.3px so
stroke-dashoffset:118.3px;
stroke-dasharray:118.3px;
#wire {
stroke-dashoffset:118.3px;
stroke-dasharray:118.3px;
stroke:red;
stroke-width:2px;
fill:none;
}
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="5 0 100 45">
<style>
</style>
<path id="wire" d="M10,10 C15,50 95,50 100,10" >
<!-- Curved path growth animation -->
<animate
attributeName="stroke-dashoffset"
begin="svg1.click"
dur="5s"
values="118.3;0"
calcMode="linear"
fill="freeze"
restart="whenNotActive" />
</animate>
</path>
<!-- Cursor -->
<polygon points="-5,-5 5,0 -5,5 -3,0" fill="red">
<!-- Animating cursor movement along a curved path -->
<animateMotion begin="svg1.click" dur="5s" repeatCount="1" calcMode="linear" rotate="auto" fill="freeze">
<mpath xlink:href="#wire" />
</animateMotion>
</polygon>
<text x="40" y="20" font-size="6px" fill="dodgerblue">Click me</text>
</svg>
<script>
var path = document.querySelector('#wire');
var len = (path.getTotalLength() );
console.log("Path length - " + len);
</script>
UPDATE
as a bonus:
Moving forward - backward
To control the direction of movement, add two buttons and an
onclick event
<div>
<button onclick="forward.beginElement()">forward</button>
<button onclick="back.beginElement()">back</button>
</div>
The direction of the cursor is controlled by a couple of arguments:
keyPoints="0;1"
keyTimes="0;1"
Animation of line growing or falling depends on the value of
stroke-dashoffset
values="118.3;0" - line growth
values="0;118.3" - decrease line`
#wire {
stroke-dashoffset:118.3px;
stroke-dasharray:118.3px;
stroke:red;
stroke-width:2px;
fill:none;
}
svg {
width:50%;
height:50%;
}
<div>
<button onclick="forward.beginElement()">forward</button>
<button onclick="back.beginElement()">back</button>
</div>
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="5 0 100 45">
<style>
</style>
<path id="wire" d="M10,10 C15,50 95,50 100,10" >
<!-- Animation of filling, growing a line (`forward`)-->
<animate id="strokeForward"
attributeName="stroke-dashoffset"
begin="forward.begin"
dur="5s"
values="118.3;0"
calcMode="linear"
fill="freeze"
restart="whenNotActive" />
<!-- String decrease animation (`back`) -->
<animate id="strokeBack"
attributeName="stroke-dashoffset"
begin="back.begin"
dur="5s"
values="0;118.3"
calcMode="linear"
fill="freeze"
restart="whenNotActive" />
</path>
<!-- Cursor -->
<polygon points="-5,-5 5,0 -5,5 -3,0" fill="red">
<!-- Animating cursor movement along a curved path (`forward`) -->
<animateMotion
id="forward"
begin="indefinite"
dur="5s"
repeatCount="1"
calcMode="linear"
rotate="auto"
fill="freeze"
keyPoints="0;1"
keyTimes="0;1"
restart="whenNotActive">
<mpath xlink:href="#wire" />
</animateMotion>
<!-- Animating cursor movement along a curved path (`back`) -->
<animateMotion
id="back"
begin="indefinite"
dur="5s"
repeatCount="1"
calcMode="linear"
rotate="auto"
fill="freeze"
keyPoints="1;0"
keyTimes="0;1"
restart="whenNotActive">
<mpath xlink:href="#wire" />
</animateMotion>
</polygon>
</svg>
Related
SVG animate reverse on second click
I am a total newbie trying to make an interactive SVG - preferably without any external scripting. The effect that I am aiming for is to have one SVG element act as an interactive toggle to make another element appear and disappear. Please find below a simple version where the text "Toggle" acts as the toggle. On click, this will animate the opacity attribute of the rectangle from 0 to 1 making it appear. <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="47.652294mm" height="10.096307mm" viewBox="0 0 47.652294 10.096307" version="1.1" id="svg8"> <style id="style861"></style> <defs id="defs2" /> <g id="layer1" transform="translate(-29.085516,-61.315985)"> <rect fill="#ff0000" opacity="0" id="rect" width="9.8317242" height="9.8317242" x="66.773796" y="61.448277"> <animate attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="toggletext.click" /> </rect> <text xml:space="preserve" style="font-size:8.46667px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.264583" x="29.085516" y="68.002762" id="toggletext"><tspan id="tspan857" x="29.085516" y="68.002762" style="stroke-width:0.264583">Toggle</tspan></text> </g> </svg> Any subsequent click will now just simply repeat that same animation. But I want any second (fourth, sixth, etc.) click to reverse the animation (i.e. make the rectangle disappear). In other words to truly act as a toggle. Any advice on how to achieve this effect with as little code and/or invisible elements as possible would be greatly appreciated. Thanks!
This is how I would do it: I'm using 2 overlapped text elements and I'm setting the pointer events to all or none on click: This way you'll click once on one text and next on the other. The rect has 2 animate elements: one animation will start when you click on the first text, the second animation will start when clicking on the second text. text{font-size:8.46667px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583;} <svg width="300" viewBox="0 0 47.652294 10.096307" id="svg8"> <g id="layer1" transform="translate(-29.085516,-61.315985)"> <rect fill="#ff0000" opacity="0" id="rect" width="9.8317242" height="9.8317242" x="66.773796" y="61.448277"> <animate attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="toggletext1.click" /> <animate attributeName="opacity" fill="freeze" from="1" to="0" dur="2s" begin="toggletext2.click" /> </rect> <text x="29.085516" y="68.002762" id="toggletext2"> <tspan x="29.085516" y="68.002762">Toggle</tspan> <set attributeName="pointer-events" from="none" to="all" begin="toggletext1.click" /> <set attributeName="pointer-events" from="all" to="none" begin=".click" /> </text> <text x="29.085516" y="68.002762" id="toggletext1"> <tspan x="29.085516" y="68.002762">Toggle</tspan> <set attributeName="pointer-events" from="none" to="all" begin="toggletext2.click" /> <set attributeName="pointer-events" from="all" to="none" begin=".click" /> </text> </g> </svg>
Add a second rectangle fade animation <animate id="hide" attributeName="opacity" fill="freeze" from="1" to="0" dur="2s" begin="indefinite" /> And add a JS trigger that toggles the animation of the appearance and disappearance of the rectangle var svg_1 = document.getElementById("svg8"), hide = document.getElementById("hide"), visable = document.getElementById("visable"); let flag = true; svg_1.addEventListener('click', function() { if (flag == true) { visable.beginElement(); flag = false; } else { hide.beginElement(); flag = true; } }); <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="47.652294mm" height="10.096307mm" viewBox="0 0 47.652294 10.096307" version="1.1" id="svg8"> <style id="style861"></style> <defs id="defs2" /> <g id="layer1" transform="translate(-29.085516,-61.315985)"> <rect fill="#ff0000" opacity="0" id="rect" width="9.8317242" height="9.8317242" x="66.773796" y="61.448277"> <animate id="visable" attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="indefinite" /> <animate id="hide" attributeName="opacity" fill="freeze" from="1" to="0" dur="2s" begin="indefinite" /> </rect> <text xml:space="preserve" style="font-size:8.46667px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.264583" x="29.085516" y="68.002762" id="toggletext"><tspan id="tspan857" x="29.085516" y="68.002762" style="stroke-width:0.264583">Toggle</tspan></text> </g> </svg>
Although I like the No-JavaScript pointer-events method; this is how I would do it: When OP says: preferably without any external scripting. I presume he means no 3rd party libraries. So I would use a Custom Element <svg-toggle> (supported in all modern browsers) that creates the SVG for any number of toggles you want To toggle animation: switch the from and to parameters on every click restart the animation <style> svg { display: inline-block; width: 30%; vertical-align: top; cursor: pointer; background: teal; color: white; } </style> <svg-toggle></svg-toggle> <svg-toggle color="yellow"></svg-toggle> <svg-toggle color="blue" label="Blue" duration=".5"></svg-toggle> <script> customElements.define('svg-toggle', class extends HTMLElement { connectedCallback() { this.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 18"> <text x="2" y="12" font-size="10px" fill="currentColor"> ${this.getAttribute("label")||'Toggle'}</text> <rect fill="${this.getAttribute("color")||'red'}" x='33' y="3" width="12" height="12"> <animate attributeName="opacity" dur="${this.getAttribute("duration")||2}s" from="0" to="1" fill="freeze" begin="indefinite"/> </rect></svg>`; this.animate = this.querySelector("animate"); this.onclick = (evt) => this.toggle(); } toggle( // method, so can be called from Javascript from = this.animate.getAttribute("from"), // optional from/to parameters to = this.animate.getAttribute("to"), ) { this.animate.setAttribute( "from", to ); this.animate.setAttribute( "to" , from ); this.animate.beginElement(); } }); </script> I don't want modern W3C standard Web Components mumbo jumbo... Then stick the JavaScript on every SVG: <svg onclick="{ let a = this.querySelector('animate'); let from = a.getAttribute('from'); a.setAttribute('from',a.getAttribute('to')); a.setAttribute('to',from); a.beginElement(); }" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 18"> <text x="2" y="12" font-size="10px" fill="currentColor">Gold</text> <rect fill="gold" x="33" y="3" width="12" height="12"> <animate attributeName="opacity" dur=".3s" from="0" to="1" fill="freeze" begin="indefinite"></animate> </rect> </svg> I don't want JavaScript See Enxaneta his pointer-events answer
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>
SVG animate gradient along path
I have the following svg that i would like to animate. I would like the red part moves along the path up to the end (so from the top right side to the left bottom side) : The problem is : obviously is quite impossible to have a gradient following a path. Here is my code so far : <svg id="fil" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 783.53 362"> <defs> <style> .cls-3{ fill: none; stroke-miterlimit:10; stroke-width:3px; } </style> <linearGradient id="light" x1="100%" y1="100%"> <stop offset="70%" stop-color="#3E3E3E"> <!-- <animate attributeName="stop-color" to="#CF4B59" from="#3E3E3E" dur="0.5s" fill="freeze" /> --> </stop> <stop offset="100%" stop-color="#CF4B59"> <!-- <animate attributeName="stop-color" from="#CF4B59" to="#3E3E3E" dur="0.5s" fill="freeze" /> --> </stop> </linearGradient> </defs> <g id="Calque_2" data-name="Calque 2"> <g id="Calque_1-2" data-name="Calque 1"> <g class="cls-2"> <path class="cls-3" id="base" d="M656.89,8.93c0,48,7.42,124.9,64.45,125.92a115.56,115.56,0,0,0,53.83-12.28c8.35-4.2,16.35-9.59,21.84-17.15s8.15-17.62,5.25-26.51c-3.12-9.53-12.16-16.28-21.87-18.83-61.57-16.19-142.83,57.7-139.63,119.4,1.23,23.69,16.72,41.59,37.61,51.29,27,12.55,60.55,13.36,89.45,8.06,12.25-2.25,25.82-5.25,37.26-10.44,12.63-5.72,32.28-20.08,28.88-36.64a18,18,0,0,0-15.63-14.59c-10.28-1.4-19.14,3.57-26.76,10-16.18,13.66-29.34,30.65-44.7,45.2a359.34,359.34,0,0,1-49.33,39.08A356.65,356.65,0,0,1,638.08,303c-35.77,14.83-90.88,29.56-123.22-.47-11.61-10.78-17.61-26.71-18.41-42.53-1.07-21.19,4.41-54.95,30-59.28,36.67-6.2,78.65,49.05,86.38,79.36,8.2,32.14-5.44,70.78-35.75,84.26-28.8,12.81-63.93,0-85.8-22.72-23.52-24.41-18.59-55.9-36.07-82.56-16-24.39-41.3-23.5-66.77-24.62" transform="translate(-52.32 -8.93)" stroke="url(#light)"/> </g> </g> </g> </svg> I tried to use "animate" to make it move along the path, but it's a vertical gradient which is applied and goes from the top to the bottom and not a gradient which follows the path. I had other ideas to overcome this : Maybe by using a second path which would be the same shape than the first one but with inverted gradient so I could make it slide along the initial path maybe Or, I could use opacity to make the final path appear but i'm not sure i will be able to make the red part move in this way... If you have some ideas to make the red part move from the top right to the bottom left it would help me a lot!
Question I would like the red part moves along the path up to the end (so from the top right side to the left bottom side) : Consider using fill-line animation with stroke-dashoffset. For clarity, I placed exactly the same curve below which will show the route of filling the line with color If this indication of the motion path is not necessary, simply remove the path id = "trace" <svg id="fil" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 783.53 362"> <defs> <style> .cls-3{ fill: none; stroke-miterlimit:10; stroke-width:3px; stroke:#E7E7E7; } #base { fill: none; stroke:crimson; stroke-width:3px; stroke-dashoffset:1732; stroke-dasharray:1732; animation: fillStroke 10s linear forwards; } #keyframes fillStroke { to {stroke-dashoffset:0;} } </style> </defs> <g transform="translate(-352.32 -8.93)"> <path id="trace" class="cls-3" d="M656.89,8.93c0,48,7.42,124.9,64.45,125.92a115.56,115.56,0,0,0,53.83-12.28c8.35-4.2,16.35-9.59,21.84-17.15s8.15-17.62,5.25-26.51c-3.12-9.53-12.16-16.28-21.87-18.83-61.57-16.19-142.83,57.7-139.63,119.4,1.23,23.69,16.72,41.59,37.61,51.29,27,12.55,60.55,13.36,89.45,8.06,12.25-2.25,25.82-5.25,37.26-10.44,12.63-5.72,32.28-20.08,28.88-36.64a18,18,0,0,0-15.63-14.59c-10.28-1.4-19.14,3.57-26.76,10-16.18,13.66-29.34,30.65-44.7,45.2a359.34,359.34,0,0,1-49.33,39.08A356.65,356.65,0,0,1,638.08,303c-35.77,14.83-90.88,29.56-123.22-.47-11.61-10.78-17.61-26.71-18.41-42.53-1.07-21.19,4.41-54.95,30-59.28,36.67-6.2,78.65,49.05,86.38,79.36,8.2,32.14-5.44,70.78-35.75,84.26-28.8,12.81-63.93,0-85.8-22.72-23.52-24.41-18.59-55.9-36.07-82.56-16-24.39-41.3-23.5-66.77-24.62" /> <g class="cls-2"> <path id="base" d="M656.89,8.93c0,48,7.42,124.9,64.45,125.92a115.56,115.56,0,0,0,53.83-12.28c8.35-4.2,16.35-9.59,21.84-17.15s8.15-17.62,5.25-26.51c-3.12-9.53-12.16-16.28-21.87-18.83-61.57-16.19-142.83,57.7-139.63,119.4,1.23,23.69,16.72,41.59,37.61,51.29,27,12.55,60.55,13.36,89.45,8.06,12.25-2.25,25.82-5.25,37.26-10.44,12.63-5.72,32.28-20.08,28.88-36.64a18,18,0,0,0-15.63-14.59c-10.28-1.4-19.14,3.57-26.76,10-16.18,13.66-29.34,30.65-44.7,45.2a359.34,359.34,0,0,1-49.33,39.08A356.65,356.65,0,0,1,638.08,303c-35.77,14.83-90.88,29.56-123.22-.47-11.61-10.78-17.61-26.71-18.41-42.53-1.07-21.19,4.41-54.95,30-59.28,36.67-6.2,78.65,49.05,86.38,79.36,8.2,32.14-5.44,70.78-35.75,84.26-28.8,12.81-63.93,0-85.8-22.72-23.52-24.41-18.59-55.9-36.07-82.56-16-24.39-41.3-23.5-66.77-24.62" > </path> </g> </g> </svg> Gradient animation option Instead of filling with color as the length of the curve increases, a gradient will perform this function <svg id="fil" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 783.53 362"> <defs> <style> .cls-3{ fill: none; stroke-miterlimit:10; stroke-width:3px; stroke:#E7E7E7; } #base { fill: none; stroke:url(#light); stroke-width:3px; stroke-dashoffset:1732; stroke-dasharray:1732; animation: fillStroke 10s linear forwards; } #keyframes fillStroke { to {stroke-dashoffset:0;} } </style> <linearGradient id="light" x1="100%" y1="100%"> <stop offset="50%" stop-color="#CF4B59"> <!-- <animate attributeName="stop-color" from="#CF4B59" to="#3E3E3E" dur="10s" fill="freeze" /> --> </stop> <stop offset="100%" stop-color="#3E3E3E"> <!-- <animate attributeName="stop-color" to="#CF4B59" from="#3E3E3E" dur="10s" fill="freeze" /> --> </stop> </linearGradient> </defs> <g transform="translate(-352.32 -8.93)"> <path class="cls-3" d="M656.89,8.93c0,48,7.42,124.9,64.45,125.92a115.56,115.56,0,0,0,53.83-12.28c8.35-4.2,16.35-9.59,21.84-17.15s8.15-17.62,5.25-26.51c-3.12-9.53-12.16-16.28-21.87-18.83-61.57-16.19-142.83,57.7-139.63,119.4,1.23,23.69,16.72,41.59,37.61,51.29,27,12.55,60.55,13.36,89.45,8.06,12.25-2.25,25.82-5.25,37.26-10.44,12.63-5.72,32.28-20.08,28.88-36.64a18,18,0,0,0-15.63-14.59c-10.28-1.4-19.14,3.57-26.76,10-16.18,13.66-29.34,30.65-44.7,45.2a359.34,359.34,0,0,1-49.33,39.08A356.65,356.65,0,0,1,638.08,303c-35.77,14.83-90.88,29.56-123.22-.47-11.61-10.78-17.61-26.71-18.41-42.53-1.07-21.19,4.41-54.95,30-59.28,36.67-6.2,78.65,49.05,86.38,79.36,8.2,32.14-5.44,70.78-35.75,84.26-28.8,12.81-63.93,0-85.8-22.72-23.52-24.41-18.59-55.9-36.07-82.56-16-24.39-41.3-23.5-66.77-24.62" /> <g class="cls-2"> <path id="base" d="M656.89,8.93c0,48,7.42,124.9,64.45,125.92a115.56,115.56,0,0,0,53.83-12.28c8.35-4.2,16.35-9.59,21.84-17.15s8.15-17.62,5.25-26.51c-3.12-9.53-12.16-16.28-21.87-18.83-61.57-16.19-142.83,57.7-139.63,119.4,1.23,23.69,16.72,41.59,37.61,51.29,27,12.55,60.55,13.36,89.45,8.06,12.25-2.25,25.82-5.25,37.26-10.44,12.63-5.72,32.28-20.08,28.88-36.64a18,18,0,0,0-15.63-14.59c-10.28-1.4-19.14,3.57-26.76,10-16.18,13.66-29.34,30.65-44.7,45.2a359.34,359.34,0,0,1-49.33,39.08A356.65,356.65,0,0,1,638.08,303c-35.77,14.83-90.88,29.56-123.22-.47-11.61-10.78-17.61-26.71-18.41-42.53-1.07-21.19,4.41-54.95,30-59.28,36.67-6.2,78.65,49.05,86.38,79.36,8.2,32.14-5.44,70.78-35.75,84.26-28.8,12.81-63.93,0-85.8-22.72-23.52-24.41-18.59-55.9-36.07-82.56-16-24.39-41.3-23.5-66.77-24.62" > </path> </g> </g> </svg>
How to Animate the fill attribute of inline SVG on mouseover/mouseout (SMIL)
I have created a simple inline SVG. Using SMIL I have tried to animate the fill attribute to pink for all paths upon mouseover of the parent SVG id. On mouseout I would like the fill to always reset. I do not want the fill animation to repeat on mouseover. I just want it to run once and 'stop' (i.e. stay pink), but, only if the animation duration completes. I have tried the following: #logoSVG { width: 100px; } <a href="javascript:void(0)" id="logo"> <svg version="1.1" id="logoSVG" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 113.7 62.2" enable-background="new 0 0 113.7 62.2" xml:space="preserve"> <path id="M" fill="#1E90FF" d="M16.1,38.9l-9-30.4H6.7C7.2,14.8,7.2,18.8,7.2,21v17.9H0V0h11.2l9,29.5l0,0L30,0h11.2v38.5h-7.6 V20.6c0-0.9,0-1.8,0-3.1c0-1.3,0-4,0.4-9h-0.4l-9.8,30.4L16.1,38.9L16.1,38.9z" /> <path id="A" fill="#1E90FF" d="M74.7,38.9l-2.7-9.4H58.2l-2.7,9.4h-9L60,0h9.8l13.9,38.9H74.7z M69.8,22.8c-2.7-8.5-4-13-4.5-14.3 c-0.4-0.9-0.4-1.8-0.9-2.7c-0.4,2.2-2.2,7.6-4.9,16.6h10.3V22.8z" /> <path id="T" fill="#1E90FF" d="M102.9,38.9h-8.1V7.2H84.6V0h29.1v6.7h-10.3v32.2H102.9z" /> <rect id="Line" y="55.9" fill="#1E90FF" width="113.7" height="6.3" /> <animate xlink:href="#M" attributeName="fill" from="#1E90FF" to="pink" dur="0.8s" begin="logoSVG.mouseover" end="logoSVG.mouseout" id="m-anim" /> <animate xlink:href="#A" attributeName="fill" from="#1E90FF" to="pink" dur="0.8s" begin="m-anim.begin + 0s" end="logoSVG.mouseout" id="a-anim" /> <animate xlink:href="#T" attributeName="fill" from="#1E90FF" to="pink" dur="0.8s" begin="m-anim.begin + 0s" end="logoSVG.mouseout" id="t-anim" /> <animate xlink:href="#Line" attributeName="fill" from="#1E90FF" to="pink" dur="0.8s" begin="m-anim.begin + 0s" end="logoSVG.mouseout" id="line-anim" /> </svg> </a> As you can see the fill does not 'stop' at the end. Am I missing something? Is mouseover/mouseout the right value to use to achieve this effect?
You can add a "fill" attribute to your -Tags, and set it to "freeze", e.g.: <animate xlink:href="#M" attributeName="fill" from="#1E90FF" to="pink" dur="0.8s" begin="logoSVG.mouseover" end="logoSVG.mouseout" id="m-anim" fill="freeze"/> From the docs: freeze - The animation effect is "frozen" when the active duration of the animation is over for the remainder of the document duration (or until the animation is restarted). Update If mouse events are triggered correctly, the freezed fill wont change back. Alternatively, you could animate each state explicitly: change the color keep the filling change the fill back to the original color I reduced the example to just one letter, for clarity: #logoSVG { border: 1px dashed #777; width: 100px; } <a href="javascript:void(0)" id="logo"> <svg version="1.1" id="logoSVG" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 113.7 62.2" enable-background="new 0 0 113.7 62.2" xml:space="preserve"> <path id="M" fill="#1E90FF" d="M16.1,38.9l-9-30.4H6.7C7.2,14.8,7.2,18.8,7.2,21v17.9H0V0h11.2l9,29.5l0,0L30,0h11.2v38.5h-7.6 V20.6c0-0.9,0-1.8,0-3.1c0-1.3,0-4,0.4-9h-0.4l-9.8,30.4L16.1,38.9L16.1,38.9z" /> <animate xlink:href="#M" attributeName="fill" from="#1E90FF" to="pink" dur="0.8s" begin="logoSVG.mouseover" end="logoSVG.mouseout" id="m-anim" fill="freeze"/> <animate xlink:href="#M" attributeName="fill" from="pink" to="pink" dur="0.8s" begin="logoSVG.mouseover+0.8s" end="logoSVG.mouseout" id="m-anim" fill="freeze"/> <animate xlink:href="#M" attributeName="fill" from="#1E90FF" to="#1E90FF" begin="logoSVG.mouseout" end="logoSVG.mouseover" id="m-anim" fill="freeze"/> </svg> </a> A sidenote: begin values like begin="m-anim.begin + 0s" seem not work in Firefox (Developer Edition 41.0a2 (2015-07-18), at least on my machine
Laggy animation on Firefox
I've replicated this effect using mask and filter. This is what I've done: Applied two masks on two different text elements, one on the left for the blurred text and one on the right for the normal text. Animated both masks and the ellipse to get the final effect. Everything works fine, however the animation is lagging on Firefox. Is there any way to make the animation smooth? CodePen body, html { height: 100%; margin: 0; background: -webkit-radial-gradient(center, ellipse, #300 10%, #000 100%); background: -moz-radial-gradient(center, ellipse, #300 10%, #000 100%); background: radial-gradient(center, ellipse, #300 10%, #000 100%); } svg { position: relative; width: 100%; left: 50%; top: 50%; transform: translate(-50%, -50%); } <svg width="100%" height="200" viewBox="0 0 700 200"> <defs> <filter id="blur"> <feGaussianBlur in="SourceGraphic" stdDeviation="3" /> </filter> <mask id="mask-left" maskUnits="userSpaceOnUse" x="0" y="0" width="700" height="200"> <path id="d1" d="M0,30 h0 c-35,15 -35,125 0,140 h0z" fill="white" /> <animate xlink:href="#d1" attributeType="XML" attributeName="d" from="M0,30 h0 c-35,15 -35,125 0,140 h-0z" to="M0,30 h700 c-35,15 -35,125 0,140 h-700z" dur="10s" repeatCount="indefinite" /> </mask> <mask id="mask-right" maskUnits="userSpaceOnUse" x="0" y="0" width="700" height="200"> <path id="d2" d="M700,30 h-672 c-35,15 -35,125 0,140 h672z" fill="white" /> <animate xlink:href="#d2" attributeType="XML" attributeName="d" from="M700,30 h-700 c-35,15 -35,125 0,140 h700z" to="M700,30 h0 c-35,15 -35,125 0,140 h0z" dur="10s" repeatCount="indefinite" /> </mask> </defs> <text mask="url(#mask-right)" x="350" y="120" fill="white" text-anchor="middle" font-size="50" font-family="Ubuntu">Magic of Filter and Masking</text> <text mask="url(#mask-left)" filter="url(#blur)" x="350" y="120" fill="white" text-anchor="middle" font-size="50" font-family="Ubuntu">Magic of Filter and Masking</text> <ellipse id="e" cx="26" cy="100" rx="25" ry="70" fill="none" stroke="#600" stroke-width="2" /> <animate xlink:href="#e" attributeType="XML" attributeName="cx" from="0" to="700" dur="10s" repeatCount="indefinite" /> </svg>
I have taken a look at your svg animation in different browsers. But it works without any lagging in FireFox and Chrome, but in Internet Explorer it's not working at all. Maybe make use of Fakesmile, this is supported for Internet Explorer.