David Khourshid - @davidkpiano
Taps, clicks, events, motion capture, physical manipulation, time, etc.
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.
A "reactive animation" is one involving
discrete changes, due to events.
3s
0s
[
]
1s
0s
3s
4.5s
5.5s
7s
npm install rxjs --save
import Rx from 'rxjs';
const second$ = Rx.Observable.create(observer => {
const intervalId = setInterval(() => {
observer.next(null)
}, 1000);
return () => clearInterval(intervalId);
});
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
});
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
});
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$)
(state, time) => newState
const animFrame$ = Rx.Observable
.interval(0, Rx.Scheduler.animationFrame);
const smoothMove$ = animFrame$
.withLatestFrom(move$, (frame, move) => move)
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
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(/* ... */);
I promise this is a really cool demo, just wait
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
😭
// ...
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.
(coming soon)
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')));
@davidkpiano