Learn CSS Animations: From A Designer's Perspective
There is not much so-called black-magic about CSS animation. You would probably pick it up in 10 minutes, but sometimes the user interface could be enhanced so much that you wont even believe that its achieved by such simple thing.
It can be as simple as a linear color transition, but also real-world masterpiece. In this post we are gonna first recap a little bit the interfaces of the animation in CSS3, but not dive too deep into it. You can re-read the awesome post here to re-familiar yourself with it. Then we are gonna reflect on how we can refactor and improve the animation, much in a designer perspective.
Recap
To specify an animation, you first define a `@keyframe`, which consists of all the frames(with different css attribute values) that the animation is going to go through, like this:
@keyframes pulse {n 0% {n background-color: #001F3F;n }n 100% {n background-color: #FF4136;n }n}n
And then add this animation to some particular elements:
.element {n animation: pulse 3s infinite;n}n
The `animation` property is a shorthand for the whole set of animation-related properties:
animation: [animation-name] [animation-duration] [animation-timing-function]n[animation-delay] [animation-iteration-count] [animation-direction]n[animation-fill-mode] [animation-play-state];n
Hope you could still remember how each of these property affect the overall animation. If not, besides MDN and CSS-Tricks, here is an awesome post with nice-illustrated examples for beginners.
Improvement / Refactor
The UI stuff was mainly taken care by designers in the past, and then encoded by programmers. There was no crossovers. But nowadays, the boundary is kind of blurring a bit, and its not just designers design and coders transcribe. The thing is, they are not talking in the language of the same world. A designer may not know `animation-timing-function`, and a programmer will possibly never fully comprehend what are those so-called art terminologies.
Which could lead to increasing cost of communication. As a programmer, have you ever thought about that if you were a designer with all the knowledge of programming, what aspects would you start working on to improve the animations?
Lets build a bouncing box and refactor the animation along the way.
We first come up with a rather stupid linear movement:
.box {n animation: bounce-1 2s linear;n}nn@keyframes bounce-1 {n 0% { transform: translateY(0); }n 50% { transform: translateY(-100px); }n 100% { transform: translateY(0); }n}n
And it looks like this:
Quite dumb, right?
Thats where the `animation-timing-function` comes to help. In the real world, most of the actions are not happening in a linear time. Say you open a drawer, it should be slow at the beginning, then fast, and in the end slow again. Thats exactly `ease-in-out`, and its actually the default value if you dont specify the property explicitly.
You can find the timing-function cheatsheet here. Here we want to change the timing-function from `linear` to `ease`. (Why? Think about the physics movement of a free fall object)
On second thought though, a bouncing ball should not be bouncing all the time. It should have INTERVAL in between each bouncing. So you might split the whole keyframes into two parts: animation and pause.
The refactored version:
.bounce-2 {n animation-name: bounce-2;n animation-timing-function: ease;n}n@keyframes bounce-2 {n 0% { transform: translateY(0); }n 30% { transform: translateY(-100px); }n 50% { transform: translateY(0); }n 100% { transform: translateY(0); }n}n
which looks like:
It looks good, but could be much more than that. Here comes something that you might havent thought of: stretch and squash. When a bouncing item touches and leaves the ground, it should squash and stretch a little bit. In the design world, we want to EXAGGERATE it to make more obvious.
Lets look at our 3rd version:
.bounce-3 {n animation-name: bounce-3;n animation-timing-function: ease;n}n@keyframes bounce-3 {n 0% { transform: scale(1,1) translateY(0); }n 10% { transform: scale(1.1,.9) translateY(0); }n 30% { transform: scale(.9,1.1) translateY(-100px); }n 50% { transform: scale(1,1) translateY(0); }n 100% { transform: scale(1,1) translateY(0); }n}n
Note that we are given the convenience that animating two properties(`scale` and `translateY`) at the same time is no different than animating a single one. Thats all taken care of under the hood, so the ONLY thing we do even care is how to mimic the animation to make it like a real-world one.
Lets move on.
In the real world, when a ball touches the ground, it would re-bounce a little bit. And the same squash and stretch also applies to this process. So we also want to add this bit into our animation:
.bounce-4 {n animation-name: bounce-4;n animation-timing-function: ease;n}n@keyframes bounce-4 {n 0% { transform: scale(1,1) translateY(0); }n 10% { transform: scale(1.1,.9) translateY(0); }n 30% { transform: scale(.9,1.1) translateY(-100px); }n 50% { transform: scale(1.05,.95) translateY(0); }n 57% { transform: scale(1,1) translateY(-7px); }n 64% { transform: scale(1,1) translateY(0); }n 100% { transform: scale(1,1) translateY(0); }n}n
The bulk of the code is increasing a little bit but still quite clear. If you go through the animation, youll note that the volume of squash and stretch is smaller than the previous one (1.05 < 1.1, 0.95 > 0.9), which is exactly what should happen in reality.
Our ultimate version looks like this:
However, you may still refactor it by customizing the cubic bezier curve. But its already way much better than the first version.
Summary
Lets rethink the process of our refactoring. We NEVER emphasize on the detail of coding. We are always talking about imitating the real-world animation. So IMHO, this is a field where IMAGINATION and DESIGN play more important roles than programming.
I hate to end this artical this way because we have just pointed out the working directions but havent dived into the details. But that would be a too big topic, so here I provide some useful links and resources if you want to learn more.
And I highly recommend you go deeper to see what great developers insights are.
最後是打廣告時間。最近寫了一個 react-animation-wrapper,一個簡單易用的,為 react 設計的動畫庫。只需要為組件包一層 wrapper 就賦予了該組件相應的動畫。如果你對開源項目有興趣,歡迎來 issue 里認領一些小 tasks,我很願意提供幫助,一起學習。repo
Resources
- 12 basic principles of animation - Wikipedia post outlining the concepts introduced in The Illusion of Life (by Disney)
- Animation in Design Systems - by Sarah Drasner
推薦閱讀: