Skip to content

Instantly share code, notes, and snippets.

@RadoslawB
Created March 20, 2020 13:39
Show Gist options
  • Save RadoslawB/de8fedf9e77f88515b71f9206e222d0f to your computer and use it in GitHub Desktop.
Save RadoslawB/de8fedf9e77f88515b71f9206e222d0f to your computer and use it in GitHub Desktop.
Rxjs animations draft
import {Component} from '@angular/core';
import {defer, interval, Observable, of, range, Subscription, timer} from 'rxjs';
import {buffer, map, mapTo, share, takeWhile, tap} from 'rxjs/operators';
import {animationFrame} from 'rxjs/internal/scheduler/animationFrame';
/**
* Without scheduler -> browser gets locked.
*/
const endlessStreamOfFramesByRange: Observable<number> = range(0, Number.POSITIVE_INFINITY, animationFrame);
const endlessStreamOfFramesByInterval: Observable<number> = interval(0, animationFrame);
const measureFPS = (obs: Observable<any>, msg = '') => obs
.pipe(
buffer(timer(0, 1000))
).subscribe((list: any[]) => console.log(`${msg} FPS: `, list.length));
// measureFPS(endlessStreamOfFramesByRange, 'Frames generator');
// measureFPS(endlessStreamOfFramesByInterval, 'Frames generator');
/**
* Call ball animation. Its non deterministic.
*/
const animateBall = (stream) => {
stream.subscribe(v => {
const ball = <HTMLElement>document.querySelector('#ball');
ball.style.transform = `translate3d(0, ${v % window.innerHeight - 100}px, 0`;
});
};
const msElapsed = defer(() => {
const start = animationFrame.now();
return interval(0, animationFrame).pipe(map(() => animationFrame.now() - start));
});
// msElapsed.subscribe(v => console.log(v));
const moveBall = v => {
const ball = <HTMLElement>document.querySelector('#ball');
ball.style.transform = `translate3d(0, ${v % window.innerHeight - 100}px, 0`;
};
// const pixelsPerSecond = 150; // velocity
// msElapsed.pipe(
// map(ms => pixelsPerSecond * ms / 1000)
// ).subscribe(v => moveBall(v));
const pixelPerSecond = v => ms => v * ms / 1000;
/**
* Now it moved with same speed, might not be smooth but wont be delayed
*/
// msElapsed.pipe(
// map(pixelPerSecond(300))
// ).subscribe(v => moveBall(v));
// adding takeWhile might do the job
const duration = ms => msElapsed.pipe(
map(elapsedMs => elapsedMs / ms),
takeWhile(v => v <= 1)
);
// duration(2000).pipe(map(v => v * window.innerHeight)).subscribe(d => moveBall(d));
// distance as higher order func
const distance = d => t => t * d;
// duration(2000)
// .pipe(
// map(distance(window.innerHeight))
// ).subscribe(d => moveBall(d));
const distanceOperator = d => map((t: number) => t * d);
// add easing or bouncing effects
const EasingFunctions = {
// no easing, no acceleration
linear: function (t) {
return t;
},
// accelerating from zero velocity
easeInQuad: function (t) {
return t * t;
},
// decelerating to zero velocity
easeOutQuad: function (t) {
return t * (2 - t);
},
// acceleration until halfway, then deceleration
easeInOutQuad: function (t) {
return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
},
// accelerating from zero velocity
easeInCubic: function (t) {
return t * t * t;
},
// decelerating to zero velocity
easeOutCubic: function (t) {
return (--t) * t * t + 1;
},
// acceleration until halfway, then deceleration
easeInOutCubic: function (t) {
return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
},
// accelerating from zero velocity
easeInQuart: function (t) {
return t * t * t * t;
},
// decelerating to zero velocity
easeOutQuart: function (t) {
return 1 - (--t) * t * t * t;
},
// acceleration until halfway, then deceleration
easeInOutQuart: function (t) {
return t < .5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
},
// accelerating from zero velocity
easeInQuint: function (t) {
return t * t * t * t * t;
},
// decelerating to zero velocity
easeOutQuint: function (t) {
return 1 + (--t) * t * t * t * t;
},
// acceleration until halfway, then deceleration
easeInOutQuint: function (t) {
return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t;
}
};
duration(2000)
.pipe(
map(EasingFunctions.easeInOutCubic),
map(distance(window.innerHeight))
).subscribe(d => moveBall(d));
// move to higher order
// const animateBall$ = time => duration(time)
// .pipe(
// map(distance(window.innerHeight)),
// tap(d => moveBall(d))
// );
//
// animateBall$(4000).subscribe()
// concatMap to sequence, mergeMap to sync them
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
sub: Subscription;
constructor() {
}
public toggleHeavyAsyncJob(): void {
if (this.sub) {
this.sub.unsubscribe();
this.sub = null;
} else {
const stream = interval(0).pipe(tap(() => this.doHeaveSyncJob()));
this.sub = measureFPS(stream, 'Heavy async job');
}
}
public doHeaveSyncJob(): void {
Array(1e7).forEach(() => Array(1e7));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment