From e637b7842e89ef7451600793dca302e410970f6f Mon Sep 17 00:00:00 2001 From: Paul Taylor Date: Tue, 29 Dec 2015 17:50:18 -0800 Subject: [PATCH] feat(scheduler): adds animationFrame scheduler. --- src/Rx.DOM.ts | 5 ++- src/scheduler/AnimationFrameAction.ts | 39 ++++++++++++++++++++++++ src/scheduler/AnimationFrameScheduler.ts | 10 ++++++ src/scheduler/AsapScheduler.ts | 1 - src/scheduler/animationFrame.ts | 3 ++ src/util/AnimationFrame.ts | 29 ++++++++++++++++++ 6 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 src/scheduler/AnimationFrameAction.ts create mode 100644 src/scheduler/AnimationFrameScheduler.ts create mode 100644 src/scheduler/animationFrame.ts create mode 100644 src/util/AnimationFrame.ts diff --git a/src/Rx.DOM.ts b/src/Rx.DOM.ts index ccc570ed70..23bb8e2a43 100644 --- a/src/Rx.DOM.ts +++ b/src/Rx.DOM.ts @@ -123,15 +123,18 @@ import {ArgumentOutOfRangeError} from './util/ArgumentOutOfRangeError'; import {ObjectUnsubscribedError} from './util/ObjectUnsubscribedError'; import {asap} from './scheduler/asap'; import {queue} from './scheduler/queue'; +import {animationFrame} from './scheduler/animationFrame'; import {AsapScheduler} from './scheduler/AsapScheduler'; import {QueueScheduler} from './scheduler/QueueScheduler'; +import {AnimationFrameScheduler} from './scheduler/AnimationFrameScheduler'; import {rxSubscriber} from './symbol/rxSubscriber'; /* tslint:enable:no-unused-variable */ /* tslint:disable:no-var-keyword */ var Scheduler = { asap, - queue + queue, + animationFrame }; var Symbol = { diff --git a/src/scheduler/AnimationFrameAction.ts b/src/scheduler/AnimationFrameAction.ts new file mode 100644 index 0000000000..2d0f5fdb3d --- /dev/null +++ b/src/scheduler/AnimationFrameAction.ts @@ -0,0 +1,39 @@ +import {Action} from './Action'; +import {FutureAction} from './FutureAction'; +import {AnimationFrame} from '../util/AnimationFrame'; + +export class AnimationFrameAction extends FutureAction { + + _schedule(state?: any, delay: number = 0): Action { + if (delay > 0) { + return super._schedule(state, delay); + } + this.delay = delay; + this.state = state; + const {scheduler} = this; + scheduler.actions.push(this); + if (!scheduler.scheduledId) { + scheduler.scheduledId = AnimationFrame.requestAnimationFrame(() => { + scheduler.scheduledId = null; + scheduler.flush(); + }); + } + return this; + } + + _unsubscribe(): void { + + const {scheduler} = this; + const {scheduledId, actions} = scheduler; + + super._unsubscribe(); + + if (actions.length === 0) { + scheduler.active = false; + if (scheduledId != null) { + scheduler.scheduledId = null; + AnimationFrame.cancelAnimationFrame(scheduledId); + } + } + } +} diff --git a/src/scheduler/AnimationFrameScheduler.ts b/src/scheduler/AnimationFrameScheduler.ts new file mode 100644 index 0000000000..5949db18fc --- /dev/null +++ b/src/scheduler/AnimationFrameScheduler.ts @@ -0,0 +1,10 @@ +import {Action} from './Action'; +import {Subscription} from '../Subscription'; +import {QueueScheduler} from './QueueScheduler'; +import {AnimationFrameAction} from './AnimationFrameAction'; + +export class AnimationFrameScheduler extends QueueScheduler { + scheduleNow(work: (x?: any) => Subscription, state?: any): Action { + return new AnimationFrameAction(this, work).schedule(state); + } +} diff --git a/src/scheduler/AsapScheduler.ts b/src/scheduler/AsapScheduler.ts index 2320a0c005..e511b2a243 100644 --- a/src/scheduler/AsapScheduler.ts +++ b/src/scheduler/AsapScheduler.ts @@ -4,7 +4,6 @@ import {Subscription} from '../Subscription'; import {QueueScheduler} from './QueueScheduler'; export class AsapScheduler extends QueueScheduler { - public scheduledId: number = null; scheduleNow(work: (x?: any) => Subscription, state?: any): Action { return new AsapAction(this, work).schedule(state); } diff --git a/src/scheduler/animationFrame.ts b/src/scheduler/animationFrame.ts new file mode 100644 index 0000000000..87366abac2 --- /dev/null +++ b/src/scheduler/animationFrame.ts @@ -0,0 +1,3 @@ +import {AnimationFrameScheduler} from './AnimationFrameScheduler'; + +export const animationFrame = new AnimationFrameScheduler(); diff --git a/src/util/AnimationFrame.ts b/src/util/AnimationFrame.ts new file mode 100644 index 0000000000..4d7af194a7 --- /dev/null +++ b/src/util/AnimationFrame.ts @@ -0,0 +1,29 @@ +import { root } from './root'; + +export class RequestAnimationFrameDefinition { + cancelAnimationFrame: (handle: number) => void; + requestAnimationFrame: (cb: () => void) => number; + constructor(root: any) { + if (root.requestAnimationFrame) { + this.cancelAnimationFrame = root.cancelAnimationFrame; + this.requestAnimationFrame = root.requestAnimationFrame; + } else if (root.mozRequestAnimationFrame) { + this.cancelAnimationFrame = root.mozCancelAnimationFrame; + this.requestAnimationFrame = root.mozRequestAnimationFrame; + } else if (root.webkitRequestAnimationFrame) { + this.cancelAnimationFrame = root.webkitCancelAnimationFrame; + this.requestAnimationFrame = root.webkitRequestAnimationFrame; + } else if (root.msRequestAnimationFrame) { + this.cancelAnimationFrame = root.msCancelAnimationFrame; + this.requestAnimationFrame = root.msRequestAnimationFrame; + } else if (root.oRequestAnimationFrame) { + this.cancelAnimationFrame = root.oCancelAnimationFrame; + this.requestAnimationFrame = root.oRequestAnimationFrame; + } else { + this.cancelAnimationFrame = root.clearTimeout; + this.requestAnimationFrame = function(cb) { return root.setTimeout(cb, 1000 / 60); }; + } + } +} + +export const AnimationFrame = new RequestAnimationFrameDefinition(root);