There is a nice micro-animation library at useanimations.com which contains some useful animations which are particularly well suited for animated icons. These are all free to use and downloadable at Lottie animations. For those that already use Lottie they can use these animations as-is. However using them is apps which don’t use Lottie, or in cases where they may need tweaking and you don’t have a designer available that can perform the necessary tweaks in After Effects, it may not be possible to do this. In this occasional series we’ll look at how to create some of these animations as Animated Vector Drawables which will show some useful AVD techniques.
The animated icon that we’re going to look at this time is based on a set of four icons which only differ by the fact the direction in which the arrow in the centre points. The example to the left shows Arrow left circle
. The only part that animates is the outer circle, and so that’s what this article will focus upon. However it is worth noting that the sample app also contains an implementation of Arrow right circle
in order to show that the animator that we use here can actually be reused across all of these implementations.
The animations in this are actually techniques that we’ve used before, however there are some things that we need to know about how to draw circles that are necessary for us to understand in order to get the desired animation that we want, and this article will cover that. Also there is something of a glitch that I discovered when trying to implement this which is only minor, but I couldn’t un-see it, so had to fix it!
Let’s begin by looking at the vector drawable for this:
The two paths here represent the outer circle, and the inner arrow. The outer circle is named circle
and this is the name that we’ll reference in order to animate things. I have quite deliberately omitted a name for the arrow to make it clear that this is not referenced externally. The drawing of the arrow uses move and line drawing commands in the SVG path data and we’ve covered these in previous articles so I won’t go over that again here.
Instead we’ll look at the circle as this contains a really useful technique in SVG – how to draw a circle. The path starts with M32,8
which is the now familiar moveto
command which moves the pen to the specified point without drawing anything. The next command is A24,24 0 1 1 31.99,8
which allows us to draw an elliptical arc. This is a bit more complicated that any of the other commands we’ve looked at before so we’ll break it down a bit. It’s worth mentioning that this command will not actually enable us to draw a complete circle, for reasons that we’ll explore, but we can draw almost a complete circle.
The elliptical arc command will draw an elliptical arc from the current location to the end location which is specified by the final two values in the command – in the example this is 31.99,8
. The radius of the arc is controlled by the first two values – in the example this is 24,24
. For a circle these values need to be the same because a circle is an ellipse which has constant radius. The third value is a rotation that is applied to the ellipse, but this has no effect when we draw a circle because of the constant radius, so we’ll ignore this. The final two values are both binary values (which can be either 0
or 1
) require a little bit of explanation.
If we consider the start and end points (the green dots) shown in the diagram, then for a given radius there can only be two possible circles that intersect these points, and these are shown in the diagram. The orientation of these circles is controlled by the relationship between the start and end points. In this case, they are aligned horizontally, so the two circles are stacked in a perpendicular direction to that. If we were to move the start and end points so that they were aligned vertically instead, then the two circles would be stacked horizontally. If we were to use start and end points that were identical, then the renderer would not have enough information determine these two distinct circles and there would actually be an infinite number of possibilities at every possible angle surrounding this single point. For that reason we cannot use the same start and end position, which is why we cannot actually render a complete circle. To get around this we position the two points very slightly apart – in the example the start point is 32,8
and the end point is 31.99,8
these are very slightly apart, but only in the horizontal axis, so the two candidate circles are stacked vertically, similarly to the diagram, only with the points much closer together, and the edges of the two circles touching. The animation starts from the very top of the circle in a clockwise direction and it will make the animation much easier if we mimic this, and that’s why we’ve chosen these specific values – they will create a circle directly below the start / end points, and because the end point is slightly to the left of the start point, the circle will be drawn in a clockwise direction.
The elliptical arc command allows us to draw arcs rather than full circles, and the two binary values that we have yet to cover are what enables us to specify precisely which.
We have already discussed that there are two circles of a given radius that can intersect both of these points, and the second of these values controls whether the top or bottom (in this case) circle is used.
This is known as the sweep flag and for those who require a detailed explanation of the mathematics behind this should check out the w3.org documentation which gives a detailed explanation. Personally I can never remember how it works, and tinker with this value until I find the correct one!
As we determined earlier that we need the bottom of these circles, we specify a value of 1
for this.
The arc that we draw will be one of two segments. The bottom circle consist of both a large and a small segment which are drawn between the start and end points:
The remaining value for the elliptical arc command specifies which of these will be drawn. It is called the large-arc-flag
the name of which makes it easy to remember that it is set to 1
to draw the large segment and 0
to draw the small one.
In our example, it is the large segment that we want to draw an almost complete circle so this is set to 1
.
So the full command of A24,24 0 1 1 31.99,8
specifies an X & Y radius of 24
a rotation of 0
, a large-arc-flag
of 1
, a sweep-flag
of 1
, and an end point of 31.99,8
.
The final command in the path is z
which is the closepath
command. This closes the path by drawing a line to the start point and will actually draw a straight line. But because the start and end points are so close together this will be imperceptible, and is why I stated earlier that it isn’t actually a perfect circle, but it’s close enough!
The important things worth repeating is that we have drawn an almost circular path that starts and ends at the topmost point, and the circle is drawn in a clockwise direction. Take a loot back at the animation at the start, and the first half of the animation, which is the transition from a complete circle to no circle, is precisely this in reverse. We achieve it by manipulating the trimPathStart
:
We have discussed trimPathStart
before in this article, so I won’t bother repeating things here. What this does is animates the trimPathStart
from 0.0
at the very beginning to 1.0
at 0.35
of the total duration, holds it there until 0.65
of the duration and then animates it back to 0.0
at end of the animation.
Some people may have noticed that this does an initial animation in the first 35% of the complete animation, and then reverses this for the final 35%, whereas that is not what is happening in the actual animation where it re-draws the circle in a clockwise direction.
The reason for this is that initially I tried animating this value and holding it at 1.0
until 0.65
then jumping back quickly to 0
, and then starting a trimPathEnd
animation, which animated from 0.0
at 0.65
of the total duration to 1.0
at the end. While this initially appeared to work, I noticed that there was a glitch creeping in no matter how quickly I made the switch back to 0.0
for the trimPathStart
value. I managed to capture this in a freeze frame:
This was only happening for a single frame in the animation, but it still caused a flash of the little dot to the left appearing at the 65% position.
That prompted me to re-think the animation slightly and rather than use trimPathEnd
(which required me to reset trimPathStart
causing the glitch) I could actually achieve the desired result by using trimPathOffset
instead. trimPathOffset
allows the other trim path operations to be offset, and so by combining this with trimPathStart
, I could actually achieve the desired result:
This holds the value of trimPathOffset
at 0.0
un to the 0.65
point and then animates it to 1.0
at the end. This happens in tandem with the second phase of the trimPathStart
animation (from 1.0
back to 0.0
). It’s a little tricky to get one’s head around how this works, but without the trimPathOffset
animation, the reverse of the trimPathStart
will cause the circle to be redrawn in an anti-clockwise direction – starting from the end point, and back around the path to the start point. The trimPathOffset
effectively offsets where we start drawing from, and bu animating this in parallel we effectively move the endpoint during the drawing, and the the value of trimPathStart
remains at the top most point of the circle and the end point moves in a clockwise direction. This gives the illusion that the second phase actually draws the circle in a clockwise direction – without a glitch in sight!:
Although the animation techniques here aren’t much that we haven’t covered before, it demonstrates that by understanding our paths an constructing them in a way which supports the animations that we’re looking to achieve, we can actually make our life much easier. This is also something that we’ll see in future articles in this series.
The source code for this article is available here.
© 2019, Mark Allison. All rights reserved.
Copyright © 2019 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.