The Observable Future

David Khourshid - @davidkpiano

Of Animations

?

?

?

Action

(Re)action

(Re)action

(Re)action

(Re)action

Taps, clicks, events, motion capture, physical manipulation, time, etc.

Functional Reactive Animations

Reactive Animations?

What are

Reactive Animations?

What are

A "reactive animation" is one involving
discrete changes, due to events.

Discrete

Continuous

By allowing programmers to
express the "what" of an interactive
animation, one can hope to then

automate the "how" of its presentation.

Reactive Animations?

What are

A "reactive animation" is one involving
discrete changes, due to events.

Value

3s

0s

PROMISE

[

]

ARRAY

1s

0s

3s

4.5s

5.5s

7s

Observable

  • Collection of values

  • Async (Time-based)

  • Immutable

  • Can be subscribed to

Observable (Stream)

  • Can Complete

Observables

  • Can Be Indefinite

  • Can Error

X

Observables with RxJS

npm install rxjs --save
import Rx from 'rxjs';

❤️

(👁🍩❤️      )

const second$ = Rx.Observable.create(observer => {
  const intervalId = setInterval(() => {
    observer.next(null)
  }, 1000);
    
  return () => clearInterval(intervalId);
});

Creating an Observable

Creating an Observable

const second$ = Rx.Observable.interval(1000);
const mouseMove$ = Rx.Observable
  .fromEvent(document.body, 'mousemove');
const mouseMovePosition$ = mouseMove$
  .map(event => ({
    x: event.clientX,
    y: event.clientY
  });

Creating an Observable

From An Event

You should not be seeing this

const hBody = new Hammer(document.body);

const pan$ = Rx.Observable
  .fromEventPattern(handler =>
    hBody.on('panleft panright', handler));

const panPosition$ = pan$.map(event => ({
  deltaX: event.deltaX,
  deltaY: event.deltaY
});

Creating an Observable

From An Event Pattern

ball$.map(
)
ball$.filter(
)
ball$.delay(100)
ball$.scan((a, b) => a + b, 0)

1

1

1

5

1

1

2

3

8

9

1+0=1

1+1=2

2+1=3

3+5=8

8+1=9

blue$.merge(yellow$)

Marble Diagrams

Animations are a Function

of state and time

(state, time) => newState

Demo!

const animFrame$ = Rx.Observable
  .interval(0, Rx.Scheduler.animationFrame);

const smoothMove$ = animFrame$
  .withLatestFrom(move$, (frame, move) => move)

Animation Frames

In RxJS

animFrame$.withLatestFrom(ball$, 
  (frame, ball) => ball)
const lerp = (current, next) => {
  const dx = next.x - current.x;
  const dy = next.y - current.y;
  const ratio = 0.1;

  return {
    x: current.x + dx * ratio;
    y: current.y + dy * ratio;
  };
};

200

300

deltaX = 300 - 200 = 100

100 * 0.1 = 10

200 + 10 = 210

210

300

100

Linear Interpolation (LERP)

Linear Interpolation (LERP)

1

0.5

0.2

ball$.scan((a, b) =>
lerp(a, b))

1

1

1

2

2

2

3

3

1

1

1

1.5

1.75

2

2.5

2.75

const lerp = // ...

const animFrame$ = // ...

const smoothMove$ = animFrame$
  .withLatestFrom(move$, (frame, move) => move)
  .scan(lerp);

smoothMove$.subscribe(/* ... */);

Go Back to the Demo

I promise this is a really cool demo, just wait

RxCSS

npm install rxcss

const mouse$ = Rx.Observable
  .fromEvent(document, 'mousemove')
  .map(({ clientX, clientY }) => ({
    x: clientX,
    y: clientY,
  }));
const style$ = RxCSS({
  mouse: mouse$,
});


style$.subscribe(...);
:root {
  --mouse-x: 0;
  --mouse-y: 0;
}

.ball {
  transform: translate(
    calc(var(--mouse-x) * 1px))
    calc(var(--mouse-y) * 1px))
  );
}

STREAM

VALUES

CUSTOM PROPERTIES

REACTIVE STYLES

You should still not be seeing this

Stop NPM installing ffs

😭

Why Animate with Observables?

Declarative

Composable

Immutable

ReactiveX is not

A Framework

+

ReactiveX Implementations

Material Motion

// ...

subscribe({
  sink: this.iconStyle$,
  source: combineStyleStreams({
    rotate$: thresholdCrossedSpring
      .value$.multipliedBy(Math.PI),
    willChange$: 'transform',
  }),
});

subscribe({
  sink: this.springIndicatorStyle$,
  source: combineStyleStreams({
    translate$: combineLatest({
      x: thresholdCrossedSpring
        .value$.multipliedBy(200),
      y: 0,
    }),
    willChange$: 'transform',
  }),
});

// ...

Reactive: Super-simple Rx-inspired API for subscribing to streams of animation events.

rxAnimate

(coming soon)

  • Uses RxJS
  • Library, not a framework
  • Works with any animation lib (or standalone)
  • Visualize and edit animations (like Origami)
  • Animation testing and debugging!
  • Follow me for updates: @davidkpiano
import lerp from 'rxanimate/animators/lerp';
import { position } from 'rxanimate/sources/mouse';
import style from 'rxanimate/renderers/style';

const smooth = lerp(0.1);
const move$ = smooth(position(document.body, 'mousemove'));
move$.subscribe(style(document.getElementById('ball')));

Thank you DevFest Florida!

@davidkpiano