Precise Timing With Web Animations API
Kirill Myshkin is a programmer at Sintesi Labs. Trying to figure it all out.More aboutKirill
JavaScript Timing Issues In Order But Off Beat
I previously viewed animations as something playful. Something that adds fuzziness to interfaces. Apart from that in good hands animation can make interfaces clearer. One property of animations on the Web that I didnt hear much about is their precision. That Web Animations API allows us to drop workarounds concerned with JavaScript timing issues. In this article youll see how not to do animations and how to coordinate animation of several elements.
Web Developer Near Me
When you work on a visual presentation of something that requires to be precise you quickly learn that you spend too much time working around JavaScripts inability to be exact about when code will actually execute. Try to implement something that relies on rhythm or shared timing and you will get my point. You might get close in certain cases but its never perfect.
One of the things I find helpful when working on complex problems is to break them down into smaller simpler problems. It happens that smaller pieces even if plenty have something that unifies them. Something that allows you to treat them uniformly. In the case of animations now that you have many more elements to deal with you need something that will guarantee a level of timing precision that would exclude the possibility of drift of elements going off-beat.
In JavaScript every task goes through a queue. Your code user interactions network events. Each task waits its turn to be performed by an event loop. That way it guarantees that things happen in order when you call a function you can be sure no sudden mouse move would inject itself in the middle. When you need things to happen later you can register an event listener or a timer.
Web Development Services
When event fires or a timer is due the task you defined in a callback goes into the queue. Once the event loop gets to it your code gets executed. Its a powerful concept that allows us to mostly ignore concurrency. It works well but you better understand how it works.
Web Animations API Where Things Are In Sync
Well look into the consequences of this in the context of animations. I encourage you to learn this topic deeper. Understanding the nature of how JavaScript work will save you hours and will keep color in your hair. Jake Archibald has done a great job of breaking it all down in his Tasks Microtasks Queues and Schedules article and more recently in his In The Loop talk at JSConf.
We can define exactly how long a timeout should wait before placing our task in the queue. What we cannot predict is what will be in the queue at the moment. It is possible to implement self-adjusting timers by checking the difference between the planned tick length and the actual moment the code is executed. That difference is applied to the next tick timeout.
Custom Web Design
It mostly works but if the required distance between ticks is measured in two-digit milliseconds or less it will rarely hit at the right moment. Also its nature that it adjusts on execution makes it hard to visualize something rhythmical. It will show the precise state when it was called but not the exact moment the state has changed.
That is because setTimeout guarantees minimum delay before a thing gets put in the queue. But theres no way to tell what will be in the queue already.
If low precision is fine for you occasionally youll get a pile. Things youve meant to be spaced out in time might be executed all at once if there were many tasks for the event loop to work on or it could all get suspended.
Shortcomings Of This Approach
Advancements in battery life come with better hardware and efficient software. Browser tabs might get suspended to reduce power consumption when not in use. When the tabs are in focus again the event loop might find itself with a handful of callbacks some of which are issued by timers in the queue to process.
Once I had to implement randomly flipping tiles for a website and one of the bugs was caused by sleepy tabs. Because each tile maintained its own timer they all fired simultaneously when the tab became active.
Notice how the top row blocks are delayed and then flip three at once. See the Pen CodePen Home Timeouts vs DocumentTimelinehttps//codepen.io/smashingmag/pen/BaYVLGo by Kirill Myshkin
Likely your code is already constrained by libraries and frameworks. That means callbacks from your timers are more likely to be put in a queue at an unfortunate moment. You might not have much opportunity for optimization as there is already much code running around.
The shortcomings above might be resolved in certain cases. You decide for yourself whats valued more in each particular project. If all your elements could be managed by a single timer you might be able to make it work.
Still I would look at requestAnimationFrame instead of timers to manage animations. The talk by Jake I linked to above illustrates it brilliantly. What it gives you is rhythm. You can be sure that your code will be executed right before the user is able to see anything. Because you have a timestamp of when your function is called you can use that to calculate the exact state you need to have.
Its up to you whats worth your time to deal with. It might well be that a particular workaround is fine and you can move on with whatever youre trying to implement. You are a better judge of what works in your case.
If the thing youre trying to implement fits into animations realm you will benefit from moving it off the queue. Lets see how we get to a place where time is king.
Meet Touch Design for Mobile Interfaces our brand-new Smashing Book on designing for mobile with proven universal human-centric guidelines. 400 pages jam-packed with in-depth user research and guidelines you can apply immediately. Shipping starts early January 2022.
In my previous article Orchestrating Complexity With Web Animations API we looked at ways to make several animations be controllable as if they were one. Now well look into how to make sure that all your animations start at the right moment.
Web Animations API introduces a notion of a timeline. By default all the animations are tied to the timeline of the document. That means animations share the same inner clock a clock that starts at the page load.
That shared clock is what allows us to coordinate animations. Whether its a certain rhythm or a pattern you dont need to worry that something will drag or go ahead of itself.
To make an animation start at a certain moment you use thestartTime property. The value of startTime is measured in milliseconds from the page load. Animations with a start time set to 1000.5 will start their playback exactly when the document timelines currentTime property equals 1000.5.
Notice the dot in the start time value Yes you can use fractions of milliseconds its that precise. However exact precision depends on browser settings.
Another useful thing is that start time can be negative as well. Youre free to set it to a moment in the future or a moment in the past. Set the value to -1000 and your animation state would be like it has been played for a second already when the page loads. For the user it would seem as if the animation had started playing before they even thought to visit your page.
To demonstrate how you can use it Ive set up a demo. Ive implemented an indicator that more than any other depends on time precision a clock. Well I did two that way one would reveal the greatness of the other. Certain things in this demo are simple enough to demonstrate the basics. Therere also some tricky parts that show you where this approach is lacking.
Digital and analog clock both implemented digitally. See the Pen Clockhttps//codepen.io/smashingmag/pen/BaYVLMj by Kirill Myshkin
The movement of the analog clock is quite simple. Three hands do the same single-turn rotation quite optimistically infinite times. Because the clock is a precise instrument Ive made the seconds and minutes hands change their position at the exact moment their corresponding values change. That helps to illustrate that they change at the exact moment as their cousins on the digital clock below.
Animation for each of the three hands differs in how long they do their rotation and in how many steps its divided. Seconds hand does a single revolution in sixty thousand milliseconds. Minutes hand does it sixty times slower than that. Hours hand because its a twenty-four-hour clock does one in equal time it takes the minutes hand to make twenty four revolutions.
To tie the clock hands operation to the same notion of time to make sure the minutes hand updates its position exactly at the moment the seconds hand finishes its rotation I used the startTime property. All the animations in this demo are set to the same start time. And thats all you need. Dont worry about the queue suspended tabs or piles of timeouts. Define it once and its done.
The digital clock on the other hand is a bit counterintuitive. Each digit is a container with overflow hidden. Inside theres a row of numbers from zero to one sitting in equal width cells. Each digit is revealed by translating the row horizontally by width of a cell times the digit value. As with the hands on the analog clock it was a question of setting the right duration for each digit. While all the digits from milliseconds to minutes were easy to do the hours required some tricks which Ill cover below.
To calculate the exact moment all the elements have to be started at which is past midnight I took the value of Date.now milliseconds since January 1 1970 stripped full days from it and adjusted it by the difference with UTC time. That left me with the number of milliseconds that have passed since the start of today. It is the only piece of data my clock needs to show what it is destined to show hours minutes and seconds.
To convert that value to the realm of the document I need to adjust it based on how much time has passed since the load of this demos page until Date.now call. To do that I subtracted it from the currentTime of the document. Applying the result to an animation means that this particular animation has been playing since midnight. Apply that to all the animations and you get a demo that has been playing since midnight.
Theoretically we could have a clock that runs since January 1 1970 52 years as of this writing but some browsers have undocumented limits for the value of animations duration. It would also feel right to apply some CSS to artificially age such clock as there wont be any other distinction from the one that has run since last night. Both of those clocks would be in perfect sync.
It is empowering to be able to implement something of this precision without any sophisticated calculations. But it only works for cases where the things youre trying to implement could be defined with keyframes. You should decide based on your particular case where it would be beneficial and where it would become more cumbersome and costly to deal with shortcomings.
An alternative to Web Animations API would be to use requestAnimationFrame or performance.now. With those you would need to calculate interpolation yourself.
If you choose to rely on Web Animations API you would have to face the fact that things fit differently into a keyframe definition. Some things might take practically no work to define because they naturally fit. Others require workarounds. Whether those workarounds add much cost or not to what youre trying to implement should dictate your approach.
The clock demo has examples of both cases. The hands themselves were the easiest thing to do. It is a keyframe of one turn rotation with steps easing function to make them tick. In the end the main movement of the demo clock took almost no work to do. I wish I could say that the digital clock was as easy to implement. But that is due to my own shortcomings I would say.
There are three examples of workarounds I had to revert to. I hope they give you an idea of what you might need to do if you go with the animations approach. They arent a good representation of Web Animations API limits they only show how a particular implementation Ive chosen had to be changed to fit. Lets see where I had to do additional work.
If you look closely each hand on the analog clock has a shadow. They add some depth and make the clock look nicer. Shadows are easily applied using box-shadow or filter CSS properties. It is animatable to a degree but where it comes short is in the animation of the shadow direction. You dont set it with angle value but by coordinates.
I couldnt find a way to implement a circular movement of a shadow using two coordinates. Instead I broke down each hand into three elements animated separately see my previous article Orchestrating Complexity With Web Animations API for that technique. The first one is a wrapper that contains the other two parts of a hand body and shadow. The wrapper is the element to which the main rotation is applied to. The body defines shape size and color of a hand while the shadow copies the body properties except for the color. Plus it has its own animation defined it rotates around the center of its hand.
Multiplying the number of elements to deal with might seem harder to do. In the case of the shadow though the fact that it was eventually separated from the hand gave more flexibility. You could style it using the CSS. And because the timing has already been dealt with having more elements doesnt make it harder.
The second workaround was required for the hours section of the digital clock. The clock is implemented with single digit elements. Three for milliseconds two for seconds and two for minutes. Hour digits dont fit in a logic of looping keyframe.
The loops arent regular because there are only four hours in the twenties. I had to introduce a wide digit to tackle this. The wide digit has the same logic as a normal digit would have only that it supports numbers from zero to ninety-nine which is plenty for this case. In the end the digital clocks hour indicator reused the same timing options as the hours hand on the analog clocks.
The third workaround is for the date complication. Now that I had wide digits element Ive reused it to show dates and just increased the duration from hours to days.
The problem with that approach was that the month length didnt map perfectly with the same length animations used in the demo. You see the calendar we use today has a messy history and is hard to fit into a simple loop. One would have to define all the exceptions of the Gregorian calendar in a single keyframe. Ill skip doing that. Im here to show you a workaround.
I chose to rely on Date instead of defining yet another flawed calendar. Who knows how many days the months will have in the future right
To make the date complication bulletproof I defined its duration to be the length of the current month. To keep using the same start time and to avoid the limit on duration value we rewind the animation to the correct date using iterationStart property.
When the month ends we need to rebuild the date complication for the next month. The right moment to do that would be when the complication animations had finished. Unlike other animations in this demo the date doesnt iterate so we will create a new date using the finished promise of the current month animation.
That approach suffers from the shortcomings described at the start of this article. But in the case of months we might close our eyes on slight imprecision.
You would have to believe my word that it works. Otherwise return to this article any last day of a month close to midnight. Who knows you could find me on the same page with eyes full of hope and fingers crossed.
Animations share the same time reference and by adjusting their startTime property you can align them to any pattern you need. You can be sure they wont drift.
Web Animations API comes with powerful tools that allow you to dramatically reduce the amount of work that your application and you have to do. It also comes with a precision that opens possibilities to implement new kinds of applications.
That power is contained in the animations realm. Whether it is suitable for your case or not is something you decide based on your needs. I hope the examples I provided in this article will give you a better idea of what path to choose.
...Read More

