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
The Observable Future of Animations
By David Khourshid
The Observable Future of Animations
- 5,744