Timely is a beautiful Android app. Waking up to see the numbers animate is like a morning bliss. I was intrigued by how the numbers are animated in that app. It’s not a simple fade-out-fade-in effect. It has some kind of folding in it — but not the same as the airport status boards. UiAutomator says that the entire block is one custom view. By profiling the GPU, I found that something is drawn to the screen consistently — even when there is a pause between animations. What could it be? That’s when Christoper Nolan came in my dreams and asked, “Are you watching closely?”.

The numbers are not directly coming from a font drawn as a TextView, instead constructed as multiple segments. The transition from one number to another happens by shape tweening the segments. Interesting! But how does a line change to a curve and back to a line? That’s where bezier curves help us. A bezier curve has two end points, say A and B, and two control points, say C1 and C2. The curve is drawn by.. alright, no math here! :P And the interesting fact is, if the C1 = A and C2 = B, we get a straight line from A to B. So a cubic bezier can behave as a curve and a line — that’s all we need.

The first part of the problem is to identify the control points. Numbers like 1 and 7 needs only two control points. However, numbers like 0, 3, 8 needs 5 control points. If we have to map 4 segments between numbers, then all the 10 digits should use five control points. The image above doesn’t show all the points in digit 1. You could assume that points 3, 4 and 5 are shared. The digit 7 doesn’t need 5 control points. But they are added so that the animation from 7 to 8 looks nice. The next part is to draw the curves with these control points. If you are using an application like Adobe Illustrator, trying this out is easy. The following image shows how the curves are created for number 3. But wait! The choice of font also matters. They choose Futura Light, which has nice curves and straight lines. If you were to try this with Roboto — good luck! ;)

Now that we’ve identified the end points and the control points for each segment, lets mash up some code.

Given a set of end points and control points, the idea is to drawn 4 cubic bezier curves. We create a path and move to the first end point. From there, we draw a bezier curve to the second end point using the given control points. This end point will be the “first” end point for the next segment, and so on. This logic would drawn the numbers without any animation. To kick in some animation bits, each transition is defined by 10 frames, called at 100ms interval. The first two and the last two frames are static — that how the number stops for a heartbeat. The in-between 6 frames maps the end points and control points for the segments between the numbers, draws a part of the transition based on the interpolated value. To put this in simpler terms, let’s take the “first” end points to be mapped to be (100, 100) and (124, 124). The first of the six frames would draw (100, 100). The second would draw ((124 – 100)/6, (124 – 100)6), that is (104, 104). The third of the six frames would draw (108, 108) and the last would finally end at (124, 124). Now extend this argument for other end and control points. This would change the curves from one to another — with in-between curves looking twisted and turned ;)

As you can see in the code, all 6 points are calculated in each draw frame. This might not be a good. Since we know the interpolation, these values could be pre-calculated and held as a giant array of points. It would also be nice to have all the points to be a float value between 0 and 1, as that would allow scaling the numbers to any sized canvas easily. If you were to deal with different width for each digit, and have other texts showing up, which could also animate, and calculate where each digit show go in a single View — that’s when I bow and respect the Timely app! They’ve done more work that what this blog post says. This post is just to give an idea of how such things can be done. Their work is a piece of art! To see how this animation works, try a HTML version here.

Hi, you wrote “Since we know the interpolation, these values could be pre-calculated and held as a giant array of points” … can you please expand this a little bit more? I’m developing a library based on this (planning to upload it on GitHub once I have it working :)) and I’m finding a little bit hard to make it more performant.

Let’s say we use a LinearInterpolator to go from 0 to 100 in 10 frames. So, the pre-calculated interpolated values will be 0, 10, 20, 30, … , 90, 100. Similarly, based on the interpolator used, and the start and end points, the in-between points that are needed can be pre-calculated and stored.

In other words, the value of expression “curr1[i-1][0] + ((next1[i-1][0] – curr1[i-1][0]) * factor)” is always going to give the same result between one frame to another. These values can be pre-calculated and stored. So the mPath.cubicTo() will not do calculation, but used to calculated values from a giant array.

Can you go into more detail on the mControlPoint1 and mControlPoint2 arrays? As I understand it those are coordinates to generate the bezier curves, as in the image of the “3”. Why are there two distinct arrays of points for that though? If it’s at all possible to maybe take a screenshot and show how the coordinates in those arrays align to for example the “3” that would be great. Just trying to wrap my head around this as I’d like to do something similar for Roboto Light.

The idea of keeping two array for the control points is for the sake of clarity. Each bezier curve has 2 control points — the first one is in first array and the second in the second array. I had to change the coordinates for 3 to make the transition smooth. Here’s an image for the final “3” I used: http://cl.ly/image/0H3v0D1T2j2b
In the image, the solid points are mentioned as 1,2,3.. The control points are named as C1,2 etc. C1,2 means “first” control point of the second curve. C2,3 stands for “second” control point of third curve.
Here’s how my array looks:
[63.25, 54], [99.5, 18], [99.5, 96], [100, 180], [56.5, 143] <– solid points.
[63, 27], [156, 18], [158, 96], [54, 180] <– first set of control points.
[86, 18], [146, 96], [150, 180], [56, 150] <– second set of control points.
Note that all these points are in a 200×200 grid.
Doing for Roboto would take more than 5 points. In fact, even for Futura, Timely used 7 points to get a smooth transition.

Thank you, for sharing this. but i would like to know how to use this view in an application.I mean i copied this code to the package and Added the it in the layout… what else should i do to show the animation in the layout?