If you missed the previous chapters, you can find them here:
- An overview of the engine, the runtime, and the call stack
- Inside Google’s V8 engine + 5 tips on how to write optimized code
- Memory management + how to handle 4 common memory leaks
- The event loop and the rise of Async programming + 5 ways to better coding with async/await
- Deep dive into WebSockets and HTTP/2 with SSE + how to pick the right path
- The building blocks of Web Workers + 5 cases when you should use them
- Service Workers, their life-cycle, and use cases
- The mechanics of Web Push Notifications
- Tracking changes in the DOM using MutationObserver
- The rendering engine and tips to optimize its performance
- Inside the Networking Layer + How to Optimize Its Performance and Security
As you surely know, animations play an essential role in the creation of compelling web apps. As users increasingly shift their attention to UX and businesses start realizing the importance of flawless, enjoyable user journeys, web applications become heavier, and feature more dynamic UI. This all requires more complex animations for smoother state transitions throughout the journey of the user. Today, this is not even considered something special. Users are becoming more advanced, by default expecting highly responsive and interactive user interfaces.
Animating your interface, however, is not necessarily straightforward. What should be animated and when, and what kind of feel should the animation have, are all tough questions.
Animating with CSS is the simplest way to get something moving on the screen.
We’ll start with a quick example on how to move a 50px element in both the X and Y axes. It’s done by using a CSS transition that’s set to take 1000ms.
move class is added, the
transform value is changed and the transition begins.
Besides the transition duration, there are options for the easing, which is essentially how the animation feels. We’ll go through easing in more detail later in this post.
If you have the following element:
The snippet above takes all the elements that have the
box class and add the
move class in order to trigger the animation.
transitionend events on the element, but only if you’re able to forego support for older versions of Internet Explorer:
Listening for the
transitioned event which is fired at the end of a transition, looks like this:
In addition to using CSS transitions, you can also use CSS animations, which allow you to have much more control over individual animation keyframes, durations, and iterations.
Keyframes are used to instruct the browser what values CSS properties need to have at given points, and it fills in the gaps.
Let’s look at an example:
This is how it looks like (quick demo) — https://sessionstack.github.io/blog/demos/keyframes/
With CSS animations you define the animation itself independently of the target element and use the animation-name property to choose the required animation.
CSS animations are still somewhat vendor prefixed, with
-webkit- being used in Safari, Safari Mobile, and Android. Chrome, Opera, Internet Explorer, and Firefox all ship without prefixes. Many tools can help you create the prefixed versions of the CSS you need, allowing you to write the unprefixed version in your source files.
By default, Web Animations only modify the presentation of an element. If you want to have your object remain at the location it was moved to, then you should modify its underlying styles when the animation has finished. This is why we’re listening for the
finish event in the above example, and set the
box.style.transform property to equal
translate(150px, 200px) which is the same as the second transformation performed by our animation.
Natural motion makes your users feel more comfortable with your web apps, which leads to a better UX.
Naturally, nothing moves linearly from one point to another. Actually, things tend to accelerate or decelerate as they move in the physical world around us since we’re not in a vacuum and there are different factors that tend to impact this. The human brain is wired to expect this kind of motion, so when you’re animating your web apps, you should use this knowledge to your advantage.
There’s terminology that you need to understand:
- “ease in” — this is when a motion starts slowly and then accelerates.
- “ease out” — this is when a motion starts quickly and then decelerates.
These two can be combined, for example “ease in out”.
Easing allows you to make animations feel more natural.
CSS transitions and animations allow you to choose the kind of easing you want to use. There are different keywords that affect the easing of the animation. You can also make your easing completely custom.
Here are some of the keywords that you can use in CSS to control the easing:
Let’s go through all of them and see what they really mean.
Animations without any kind of easing are referred to as linear.
Here is how a graph of a linear transition looks like:
As time goes by, the value increases in equal amounts. With linear motion, things tend to feel unnatural. Generally speaking, you should avoid linear motion.
This is how a simple linear animation can be achieved:
transition: transform 500ms linear;
As mentioned earlier, easing out causes the animation to start more quickly compared to the linear ones, while it slows down at the end. This is how its graph looks like:
In general, easing out is best used for UI work because the quick start gives your animations a feeling of responsiveness, while the slow-down at the end feels natural due to the inconsistent motion.
There are many ways to achieve an ease out effect, but the simplest is the
ease-out keyword in CSS:
transition: transform 500ms ease-out;
These are the opposite of the ease-out animations — they start slowly and end fast. This is how their graph looks like:
Compared to ease-out animations, ease-ins can feel unusual because they create a feeling of unresponsiveness since they start slowly. The fast end can also create a strange feeling, since the whole animation is accelerating while objects in the real world tend to decelerate when stopping suddenly.
To use an ease-in animation, similarly to ease-out and linear animations, you can use its keyword:
transition: transform 500ms ease-in;
This animations is a combination of ease-in and ease-out. This is how its graph looks like:
Don’t use animation durations that are too long, as they make it feel as if your UI is unresponsive.
To get an ease-in-out animation, you can use the
ease-in-out CSS keyword:
transition: transform 500ms ease-in-out;
You can define you own easing curves which provides a lot more control over the feel your project’s animations create.
Let’s see hоw Bézier curves work. A Bézier curve takes four values, or more precisely it takes two pairs of numbers. Each pair describes the X and Y coordinates of a cubic Bézier curve’s control point. The starting point of the Bézier curve has a coordinate (0, 0) and the end coordinate is (1, 1). You can set both of the pair numbers. The X values for the two control points must be in the [0, 1] range, and each control point’s Y value can exceed the [0, 1] limit, although the spec isn’t clear by how much.
Even slight changes in the X and Y value of each control point gives you a completely different curve. Let’s take a look at two graphs of Bézier curves with points having close but different coordinates.
As you see, the graphs are quite different. The first control points have a
(0.045, 0.183) vector difference, while the second control points have a (-0.427, -0.054) difference.
This is how the CSS for the second curve looks like:
transition: transform 500ms cubic-bezier(0.465, 0.183, 0.153, 0.946);
The first two numbers are the X and Y coordinates of the first control point, and the second two numbers are the X and Y coordinates of the second control point.
You should maintain 60fps whenever you are animating, otherwise it will negatively impact your users’ experience.
As everything else in the world, animations do not come free. Animating some properties is cheaper than others. For example, animating
height of an element changes its geometry and may cause other elements on the page to move or change size. This process is called layout. We have discussed layout and rendering in more detail in one of our previous posts.
In general, you should avoid animating properties that trigger layout or paint. For most modern browsers, this means limiting animations to
You can use
will-change to inform the browser that you intend to change an element’s property. This allows the browser to put the most appropriate optimizations in place ahead of when you make the change. Don’t overuse
will-change, however, because doing so can cause the browser to waste resources, which in turn causes even more performance issues.
will-change for transforms and opacity looks like this:
will-change: transform, opacity;
The browser support for Chrome, Firefox and Opera is quite good.
You probably saw it coming already — there is no right or wrong answer to this question. You just have to keep the following things in mind:
- Changes to
opacitycan, in many cases, also be handled by the compositor thread.
Great animations add a layer of enjoyment and engagement to your projects for your users. You can animate pretty much anything you like, whether that’s widths, heights, positions, colors, or backgrounds, but you need to be aware of potential performance bottlenecks. Poorly chosen animations can negatively affect user experience, so animations need to be both performant and appropriate. Animate as less as possible. Animate only to make your UX feel natural but don’t over-animate.
Don’t just animate something because you can. Instead, use strategically placed animations to reinforce the user interactions. Avoid animations that interrupt or obstruct the user’s activity unnecessarily.
The only thing worse than animations that are poorly placed are those that cause the page to stutter. This type of animation leaves users feeling frustrated and unhappy.
We go pretty easy on using animations in SessionStack. In general, we follow the practices mentioned above but we also have a few more scenarios where we utilize animations due to the complexity of our UI. SessionStack has to recreate as a video everything that happened to end users at the time they experienced an issue while browsing a web app. To do this, SessionStack leverages only the data that was collected by our library during the session: user events, DOM changes, network requests, exceptions, debug messages, etc. Our player is highly optimized to properly render and make use of all the collected data in order to offer a pixel-perfect simulation of end users’ browser and everything that happened in it, both from a visual and technical standpoint.
To ensure the replication feels natural, especially with long and heavy user sessions, we use animations to properly indicate loading/buffering and follow best practices on how to implement them so that we don’t take up too much CPU time and leave the event loop free to render the sessions.
There is a free plan if you’d like to give SessionStack a try.