Created
March 20, 2020 13:39
-
-
Save RadoslawB/de8fedf9e77f88515b71f9206e222d0f to your computer and use it in GitHub Desktop.
Rxjs animations draft
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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