SVG tricky keyTimes behaviour - animation

I am making an animated SVG donut chart. My best try for the moment is here. But this is not exactly what i want. I want the elements appear at the start, then fade out before another one has appeared on the screen, then wait for all other elements to appear and fade out, and then to start this cycle again from the first element. As you see, now elements are fading in/out together, only their animation start times differ. I tried values/keyTimes attributes in another try. But the animation is not working in any browser i know.
Here is the problem code in first SVG:
<animate
attributeName="opacity"
begin="0ms" //This is for the first element, for anothers it differs
//I wish there is `pause` attribute, to pause animation repiting...
dur="3000ms"
from="0.7"
to="0"
repeatCount="indefinite"/>
Here is another code sample
<animate
attributeName="opacity"
begin="0ms"
keyTimes="0,0.2,1"
values="0.7,0,0"
dur="15000ms"
repeatCount="indefinite"/>
As you see, i tried to increase animation duration, but to end fading of element at 20% (i have five elements to fade) of this time. But it does not work.
Tried reordering attributes, using from and to with two-valued keyTimes and long dur with no effect. There must be some cunning trick here...
P.S. This is almost what i want, but i need animation to repeat.

The problem is commas in keyTimes and value attributes - need to use semicolons and read docs twice.

Related

Is there an animation-play-state property in SVG animation?

I am animating svg elements using <animate>. Is there an equivalent property to the animation-play-state property in CSS animation in SVG animation? I've looked at this documentation Mozilla docs and it seems that there isn't one, but I just want to make sure.
This website Introduction to SMIL animation in SVG is useful. From it I learned that beginElement() and endElement() in a JavaScript script, can be used to start and end animations.
The begin attribute should be set to indefinite. An example is shown below.
JS
function start(id){
D.getElementById(id).beginElement()
}
SVG
<ellipse fill="lightpink" onclick="start('A')" cx="40" cy="140" rx="22" ry="14">
<animate id="A" attributeName="cx" dur="3s" begin="indefinite" values="40;400;40"/>
</ellipse>
If an animation tag A contains the attribute end="indefinite" then A.endElement() will succeed in terminating that animation.
Also, a JavaScript call to document.documentElement.pauseAnimations() will stop all SMIL animations within an SVG document, freezing objects at their current position.
document.documentElement.unpauseAnimations() resumes SMIL animations from whatever position they were paused at. More about pauseAnimations() and unpauseAnimations() here at W3C and MDN Docs: SVGSVGElement.

Skrollr.js relative key frames and best practices

I'm not sure if something I want to do is possible with skrollr, it doesn't seem to be possible but maybe I am misunderstanding. I would like to be able to describe keyframes in scroll points relative to other keyframe events, as in "start this animation event 500px after another element's animation event", and wondering what the best practice is. I am working on a large page of multiple sections of animated content. Each section scrolls to the top, then becomes temporarily fixed as multiple animations occur within the section on many key frames, then once that section's animations are done it scrolls up off the screen and the next section comes into view and does it's own animating, and so on (not unlike the main skrollr demo but more complex with many more animation events). My main issue is that I want to be able to easily edit in the future each sections' animation timings independently, for example to adjust little details here and there as needed, anticipating some back-and-forth with the artists and clients I'm working with. But when relying on an absolute scrollTop for all timings, this becomes problematic because one little timing change means I have to adjust all subsequent timings throughout the remainder of the page. To get around this I am using constants to denote the start of each animated section so that at least I can have each animated section be timed relative to its start, as in:
<style type="text/css">
#fixedanimatedcontent1, #fixedanimatedcontent2 {position: fixed;}
</style>
<section id="fixedanimatedcontent1" data-_fixedanimstart1--630="top:100%;" data-_fixedanimstart1="top:0%;" data-_fixedanimstart1-1500="top:0%;" data-_fixedanimstart2="top:-100%;">
<div data-_fixedanimstart1="width: 0%;" data-_fixedanimstart1-470="width: 100%"></div>
<img src="x.png" data-_fixedanimstart1-270="opacity: 0;" data-_fixedanimstart1-670="opacity: 1;" data-_fixedanimstart1-1170="opacity: 0;" />
</section>
<section id="fixedanimatedcontent2" data-_fixedanimstart2--630="top:100%;" data-_fixedanimstart2="top:0%;" data-_fixedanimstart2-2000="top:0%;" data-_fixedanimstart3="top:-100%;">
<img src="y.png" data-_fixedanimstart2-500="opacity: 0;" data-_fixedanimstart2-1000="opacity: 1;" data-_fixedanimstart2-1500="opacity: 0;" />
</section>
But even still, for complex sequences making a small timing change will be a bit of a mess, requiring at the least changing all key frame offsets within that section, and probably also changing constant values. Looking at my example above, 2 questions:
1) Is there a way to describe a relative keyframe that, let's say, begins 500px after section #fixedanimatedcontent2's top=0%? I know I can do data-top, but in my setup #fixedanimatedcontent2 becomes fixed at the top for some time once it hits the top. So how do I describe a keyframe that I want to begin 500px of scrolling after #fixedanimatedcontent2 hits data-top? Is this not possible within the syntax of "relative key frames" since offsets are only relative to element position in the viewport? If this were doable somehow, I wouldn't have to rely on constants so much...
2) How about a keyframe that begins when section #fixedanimatedcontent2's <img> reaches an opacity of 1? That way I could later if I needed make a change in the length of that <img>'s opacity interpolation without having to change all subsequent key frame timings. Pretty sure this is not possible but had to ask...
So: Am I misunderstanding what is the best way to do this sort of relative sequencing most efficiently? Or is using constants as in the example above the best practice?
(this was a really verbose post, sorry!)
It is very well possible in skrollrjs. I am telling nothing different from skrollrjs documentation. If you look carefully there, there are two modes for keyframes
absolute
relative
I think you wanted to use relative mode. So, i would answer each of your questions orderly.
1) Is there a way to describe a relative keyframe that, let's say, begins 500px after section #fixedanimatedcontent2's top=0%? I know I can do data-top, but in my setup #fixedanimatedcontent2 becomes fixed at the top for some time once it hits the top. So how do I describe a keyframe that I want to begin 500px of scrolling after #fixedanimatedcontent2 hits data-top? Is this not possible within the syntax of "relative key frames" since offsets are only relative to element position in the viewport? If this were doable somehow, I wouldn't have to rely on constants so much...
Answer:- It is possible to do that. In relative mode of work, you can define your relative targets and you can define css accordingly. So for your element #fixedanimatedcontent2 when reaches top i would use like following html
You can use following cheatseet for this. This is helpful.
https://ihatetomatoes.net/skrollr-cheatsheet/
I think all you need is already described if you read carefully
https://github.com/Prinzhorn/skrollr

Mozilla(Firefox) , Marker , Multiple SVGs

I've enbedded d3's force directed graph layout into extjs tabs so that each time a new tab gets added a new graph svg gets generated.
No Problemo so far.
Now I intended to turn the graph into a directed one (by adding a marker and tell the lines to use it)
Each generated svg elements is following this pattern:
<svg width="100%" height="100%">
<defs><marker id="end-arrow" viewBox="0 -5 10 10" refX="6" markerWidth="3" markerHeight="3" orient="auto"><path d="M0,-5L10,0L0,5" fill="#ccc"></path></marker>
</defs>
<g transform="translate(4,0) scale(1)"><line class="link" sig="30.84" style="stroke-width: 3;" x1="538" y1="347" x2="409" y2="467" marker-end="url(#end-arrow)"></line>
...
</g>
</svg>
With Crome everything works just fine.
So I arrived at the concusion that the structur and
the way I generate the svgs should be more or less correct.
But with Firefox the Markers will only show for the first svg. (the first tab)
All other svgs won't show any Arrowheads.
"Inspect Elements" tells me the Markers are there and that the lines are refering to them.
And this is where I'm running out of Ideas where or what to look for. :(
You have multiple non-distinct IDs within the same html or svg document. This is invalid, different UAs respond differently but as you're not allowed to do this, it doesn't really matter that they are inconsistent.

how do I use animateMotion on only a part of a path?

I have a path that I want to animate an element (image in this case) along. But I just want it to animate along a part of the path. Wait there until the user interacts and then animate it along the rest of the path.
With other animations you could always specify from and to attributes. My thought was that you could do it like this:
JavaScript
//user press some key
animationElement.setAttribute("from", "0.0"); //beginning of path
animationElement.setAttribute("to", "0.5"); //middle of path
animationElement.beginElement();
...
//user presses another key
animationElement.setAttribute("from", "0.5"); //middle of path
animationElement.setAttribute("to", "1.0"); //end of path
animationElement.beginElement();
svg
<path id="myPath" d="m1119,29l-80,137l-601,52l-152,318l-381,218" stroke="#000000" fill="none" pathLength="1" />
<image xlink:href="someImage.png" width="231" height="262">
<animateMotion fill="freeze" begin="indefinite" end="indefinite" from="0" to="0.4">
<mpath xlink:href="#myPath" />
</animateMotion>
</image>
Have you considered splitting the motionpath in two parts, and then changing the xlink:href on the mpath element?
I have been trying to do the same thing without success.
I would not expect the from/to described in your question to work because these attributes are not appropriate.
I have been experimenting with keyTimes and keyPoints but these do not seem to work properly (when viewed in chrome or Firefox). Any inconsistent settings break the animation and any consistent settings just have the motion moving at a constant rate across the whole path whatever they are set to.
I'm not sure if this is a bug in the svg implementation or not. For example:
keyTimes="0;0.2;0.3;0.9;1" keyPoints="0;0.5;0.6;0.7;1" produces smooth movement across the path.
If they worked I think that keyTimes="0;1" keyPoints="0;0.5" would achieve the desired objective.
Currently I'm having to use multiple paths and thread the animations together appropriately which is complex and annoying but at least it gets the job done.

SVG text - total length changes depending on zoom

In SVG (for web-browsers), if i add a <text>-element and add some text to it the total rendered width of the text string will change depending on the scale of the text.
Lets say i add "mmmmmmmmmmmmmmmmmmmmmmmmmmA" as text, then i want to draw a vertical line(or other exactly positioned element) intersecting the very last character. Works fine but if i zoom out the text will become shorter or longer and the line will not intersect the text in the right place anymore. The error can be as much as +/- 5 characters width which is unacceptable. The error is also unpredictable, 150% and 160% zoom can add 3 characters length while 155% is 2 charlengths shorter.
My zoom is implemented as a scale-transform on the root element of my canvas which is a <g>.
I have tried to multiply the font-size with 1000x and scale down equally on the zoom-transform and vice versa in case it was a floating point error but the result is the same.
I found the textLength-attribute[1] which is supposed to adjust the total length so the text always end where i choose but it only works in Webkit. Firefox and Opera seems to not care at all about this value (haven't tried in IE9 yet).
Is there any way to render text exactly positioned without resorting to homemade filling of font-outlines?
[1] http://www.w3.org/TR/SVG11/text.html#TextElementTextLengthAttribute
Update
Snippet of the structure i'm using
<svg>
<g transform="scale(1)"> <!--This is the root, i'm changing the scale of this element to zoom -->
<g transform="scale(0.014)"> <!--This is a wrapper for multi-line text, scaling, other grouping etc -->
<text font-size="1000" textLength="40000">ABDCDEFGHIJKLMNOPQRSTUVXYZÅÄÖabcdefghijklmnopqrstxyzåäö1234567890</text>
</g>
</g>
Interesting, since the webkit builds I have seem to fail on textLength (w3c testcase). Could you post your example?
Speaking for Opera we do support the 'textLength' attribute, as documented here.
The other option you have is to use the getBBox method to find a good position for drawing your line, that should work in all the browsers.

Resources