Managing animations
@opherv
YGLF 2019
(sanely)
in
(insanely)
complex apps
I'm @OpherV
"Have you tried clearing your cache"?
"No Mom, I don't know why your computer is running slow"
"Don't just add Z-index 999999 that won't work"
Creative Developer
You may know me from my greatest hits:
Hi!
Managing animations
(sanely)
in
(insanely)
complex apps
State
Animation
What is animation?
Frames over time
Frames over time
F( ) =
time
frame
UI Animation
is Dynamic
Microinteractions for to-do list app/ Jakub Antalík
https://dribbble.com/shots/3167358-Microinteractions-for-to-do-list-app
It changes based on
App data
User input
UI Animation is a tool to convey information
Meaningful Motion with Action-Driven Animation / Tobias Ahlin
http://tobiasahlin.com/blog/meaningful-motion-w-action-driven-animation/
Meaningful Motion with Action-Driven Animation / Tobias Ahlin
http://tobiasahlin.com/blog/meaningful-motion-w-action-driven-animation/
Meaningful Motion with Action-Driven Animation / Tobias Ahlin
http://tobiasahlin.com/blog/meaningful-motion-w-action-driven-animation/
What is state?
Inspired by Jing Cheng's talk
https://youtu.be/nYkdrAPrdcw
Inspired by Jing Cheng's talk
https://youtu.be/nYkdrAPrdcw
Inspired by Jing Cheng's talk
https://youtu.be/nYkdrAPrdcw
Inspired by Jing Cheng's talk
https://youtu.be/nYkdrAPrdcw
State =
current snapshot of the app data
Handling app state
is hard!
F(old state, action) = new state
lessFancySelect:{
selectedOptions: [],
possibleOptions: ['Principle',
'Sketch',
'Framer',
'Photoshop'
]
}
Example data structure:
User clicks on option 2
option 1 gets added to the state
view renders
view renders
User clicks on option 1
option 2 gets added to the state
https://dribbble.com/shots/4371972-Graphics-Editor-Selection-Drop-Down
User clicks on option:
option gets added to the state
option disappear animation
Squash animation + Rectangle appear animation
Label expand animation
What happens if
a user clicks a few options rapidly?
What happens when if
a user clicks a few options rapidly?
User clicks on option 2
option 1 gets added to the state
option disappear animation starts
????
User clicks on option 1
option 2 gets added to the state
// state
lessFancySelect:{
selectedOptions: ['Sketch']
}
// state
lessFancySelect:{
selectedOptions: ['Sketch,
'Photoshop']
}
// state
lessFancySelect:{
selectedOptions: []
}
😔
Nobody defined what should happen to the animation in this scenario
😭
Animating an element that wasn't even added to the DOM yet
😱
Animating an element that was already removed from the DOM
Animations might target same elements and clash with one another
😕
This is what I mean when I say
"(insanely) complex apps":
- Multiple, rapid state changes in multiple components
- Animations span across multiple components and can be activated at arbitrary times
The problem:
F(time) = frame
F(old state, action) = new state
Story time
Solution 1:
Skipping
Animation 1
Animation 2
action
(with fast-forward)
Pro: Animation is always updated relative to the state
Solution 1:
Skipping
Con: Jumping between states/animation looks janky
Solution 2:
Queuing
Animation 1
Animation 2
action
Pro: Animations will always flow fluidly
Solution 2:
Queuing
Con: UI might not reflect the current state;
Animations play "catch-up" to the state
Solution 3 :
Additive animation
Animation 1
Animation 2
action
http://ronnqvi.st/multiple-animations
Skipping
http://ronnqvi.st/multiple-animations
Additive
Pro: Seamlessly joins two animations in a natural way
Solution 3:
Additive Animation
Con: Easy for single-element position-animation;
however most animation systems don't fit into this paradigm
"Solution" 4 :
Limiting access to UI
Animation 1
Animation 2
furious clicking
"Solution" 4 :
Limiting access to UI
Abusing your users
action
Pro: Makes your designer and PM happy
"Solution" 4:
Limiting access to UI
Con: Makes your users cry
Which to pick?
It's all about
managing expectations
Starting to
untangle the mess
implementation
Animation Timelines
https://github.com/EkoLabs/react-animation-orchestrator
Happy to announce!
1. Create animation sequences
2. Register these sequences to a component
3. Defining scenarios that decide when to trigger the animations
Working with react-animation-orchestrator:
PageComponent
FancyComponent
When PageComponent props change ->
animate FancyComponent
then animate SomeOtherComponent
The Goal:
SomeOtherComponent
// step 1: defining an animation generator function
import { TimelineMax } from 'gsap';
const lookAtMeAnimation = (ref, options) => {
let tl = new TimelineMax();
let myEl = ref.current;
tl.to(myEl, 0.5, {
scale: 1.5,
rotating: '45deg',
transformOrigin: 'center',
opacity: 0.7,
});
return tl;
};
// step 2: registering the animation with a component
import React from "react";
import { attachAnimation } from "@ekolabs/react-animation-orchestrator";
class FancyComponent extends React.Component {
constructor(props){
super(props);
this.ref = React.createRef();
this.props.registerAnimation('lookAtMe', lookAtMeAnimation, this.ref);
}
render(){
return <div ref={this.ref}>Attention-grabbing element</div>
}
}
export default attachAnimation(FancyComponent);
// step 2: registering the animation with a component
import React from "react";
import { attachAnimation } from "@ekolabs/react-animation-orchestrator";
class FancyComponent extends React.Component {
constructor(props){
super(props);
this.ref = React.createRef();
this.props.registerAnimation('lookAtMe', lookAtMeAnimation, this.ref);
}
render(){
return <div ref={this.ref}>Attention-grabbing element</div>
}
}
export default attachAnimation(FancyComponent);
// step 2: registering the animation with a component
import React from "react";
import { attachAnimation } from "@ekolabs/react-animation-orchestrator";
class FancyComponent extends React.Component {
constructor(props){
super(props);
this.ref = React.createRef();
this.props.registerAnimation('lookAtMe', lookAtMeAnimation, this.ref);
}
render(){
return <div ref={this.ref}>Attention-grabbing element</div>
}
}
export default attachAnimation(FancyComponent);
// step 3: Configuring a scenario for a "controller"
// element to queue the animation
import { attachAnimation } from "@ekolabs/react-animation-orchestrator";
class PageComponent extends React.Component {
render(){
return (
<div>
<FancyComponent/>
<SomeOtherComponent />
<MoreComponents />
</div>
)
}
}
export default attachAnimation(PageComponent, [
{
trigger: {
select: props => props.grabAttention,
value: false,
nextValue: true
},
animations: ['lookAtMe', 'animationOnSomeOtherComponent']
}
]);
The problem:
F(time) = frame
F(old state, action) = new state
Possible solutions:
Cut and skip animation
Queue animation
Additive animtion
Thanks!
@OpherV
slides.com/opherv/yglf2019
The Pac-Man rule
by @ericholscher
Addendum
Thanks again!
@OpherV
slides.com/opherv/yglf2019
Managing animations (sanely) in (insanely) complex apps
By Opher Vishnia
Managing animations (sanely) in (insanely) complex apps
- 1,671