{"size": 2624, "ext": "js", "max_stars_repo_path": "app/operations.js", "max_stars_repo_name": "andydevs/reactive-animations", "max_stars_repo_stars_event_min_datetime": null, "max_stars_repo_stars_event_max_datetime": null, "max_issues_repo_path": "app/operations.js", "max_issues_repo_name": "andydevs/reactive-animations", "max_issues_repo_issues_event_min_datetime": "2022-02-27T09:59:09.000Z", "max_issues_repo_issues_event_max_datetime": "2022-02-27T09:59:09.000Z", "max_forks_repo_path": "app/operations.js", "max_forks_repo_name": "andydevs/reactive-animations", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": null, "max_issues_count": 1, "max_forks_count": null, "avg_line_length": 25.9801980198, "max_line_length": 74, "alphanum_fraction": 0.612804878}
/**
 * Using rxjs to animate javascript components
 * 
 * Author:  Anshul Kharbanda
 * Created: 9 - 7 - 2020
 */
import { 
    animationFrameScheduler, 
    combineLatest, 
    scheduled, 
    Subscriber, 
    Subscription 
} from 'rxjs'
import { 
    observeOn, 
    repeat, 
    scan, 
    map 
} from 'rxjs/operators'

/**
 * An operator that toggles a boolean on and off when
 * The source observable emits
 * 
 * @param {boolean} initial initial state of toggler
 */
export function toggleState(initial=false) {
    return scan(state => !state, initial)
}

/**
 * Meta subscriber that manages a subscription to an observable
 * Subscribing when the input is true and unsubscribing when it
 * is false
 * 
 * @param {Observable<any>} observable$ the observable to subscribe to
 * @param {Subscriber} subber subscriber that subscribes to the observable
 * 
 * @return {Subscriber} the subscriber that implements toggling
 */
export function conditionalSubscription(observable$, subber) {
    return {
        subscription: Subscription.EMPTY,
        next(state) {
            if (state && this.subscription.closed) {
                this.subscription = observable$.subscribe(subber)
            }
            else if (!this.subscription.closed) {
                this.subscription.unsubscribe()
            }
        },
        error() {
            if (!this.subscription.closed) {
                this.subscription.unsubscribe()
            }
        },
        complete() {
            if (!this.subscription.closed) {
                this.subscription.unsubscribe()
            }
        }
    }
}

/**
 * Operator that maintains a position which is interpolated
 * over time using the distance between the stored position
 * and the next position
 * 
 * @param {float} alpha alpha parameter for interpolation
 * 
 * @return {Operator} interpolation operator
 */
export function interpolate(alpha) {
    let beta = 1 - alpha
    return scan(
        (pos, next) => ({
            x: alpha * next.x + beta * pos.x,
            y: alpha * next.y + beta * pos.y
        })
    )
}

/**
 * Operator that returns an observable that emits the last 
 * emitted value from the given observable every animation 
 * frame.
 * 
 * @return continuous animation operator
 */
export function continuousAnimationSignal() {
    const frameSignal$ = scheduled([null], animationFrameScheduler)
        .pipe(repeat())
    return function(source$) {
        return combineLatest([frameSignal$, source$])
            .pipe(
                map(([_, out]) => out),
                observeOn(animationFrameScheduler)
            )
    }
}