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']
    }
]);

Demo!

(Time to pray to the Demo Gods)

 🙏🙏🙏

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