mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 03:32:21 +08:00
Merge branch 'master' into list-border-refactor
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
import {CSS} from '../util/dom';
|
import {CSS} from '../util/dom';
|
||||||
import {extend} from '../util/util';
|
import {extend} from '../util/util';
|
||||||
|
import {FastDom} from '../util/fastdom';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,8 +25,10 @@ import {extend} from '../util/util';
|
|||||||
|
|
||||||
export class Animation {
|
export class Animation {
|
||||||
|
|
||||||
constructor(ele, opts={}) {
|
constructor(ele, opts={}, fastdom=null) {
|
||||||
this.reset();
|
this.reset();
|
||||||
|
this._fastdom = fastdom;
|
||||||
|
|
||||||
this._opts = extend({
|
this._opts = extend({
|
||||||
renderDelay: 16
|
renderDelay: 16
|
||||||
}, opts);
|
}, opts);
|
||||||
@ -251,10 +254,14 @@ export class Animation {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self._duration > 16) {
|
if (self._duration > 16 && this._opts.renderDelay > 0) {
|
||||||
// begin each animation when everything is rendered in their starting point
|
// begin each animation when everything is rendered in their starting point
|
||||||
// give the browser some time to render everything in place before starting
|
// give the browser some time to render everything in place before starting
|
||||||
setTimeout(kickoff, this._opts.renderDelay);
|
if (this._fastdom) {
|
||||||
|
this._fastdom.write(kickoff);
|
||||||
|
} else {
|
||||||
|
setTimeout(kickoff, this._opts.renderDelay);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// no need to render everything in there place before animating in
|
// no need to render everything in there place before animating in
|
||||||
@ -503,14 +510,19 @@ export class Animation {
|
|||||||
return copy(new Animation(), this);
|
return copy(new Animation(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose(removeElement) {
|
||||||
let i;
|
let i;
|
||||||
|
|
||||||
for (i = 0; i < this._chld.length; i++) {
|
for (i = 0; i < this._chld.length; i++) {
|
||||||
this._chld[i].dispose();
|
this._chld[i].dispose(removeElement);
|
||||||
}
|
}
|
||||||
for (i = 0; i < this._ani.length; i++) {
|
for (i = 0; i < this._ani.length; i++) {
|
||||||
this._ani[i].dispose();
|
this._ani[i].dispose(removeElement);
|
||||||
|
}
|
||||||
|
if (removeElement) {
|
||||||
|
for (i = 0; i < this._el.length; i++) {
|
||||||
|
this._el[i].parentNode && this._el[i].parentNode.removeChild(this._el[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reset();
|
this.reset();
|
||||||
|
@ -2,7 +2,6 @@ import {Title} from 'angular2/angular2';
|
|||||||
|
|
||||||
import {ClickBlock} from '../../util/click-block';
|
import {ClickBlock} from '../../util/click-block';
|
||||||
import {ScrollTo} from '../../animations/scroll-to';
|
import {ScrollTo} from '../../animations/scroll-to';
|
||||||
import * as dom from '../../util/dom';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -11,11 +10,10 @@ import * as dom from '../../util/dom';
|
|||||||
*/
|
*/
|
||||||
export class IonicApp {
|
export class IonicApp {
|
||||||
|
|
||||||
/**
|
constructor(fastdom) {
|
||||||
* TODO
|
this._fastdom = fastdom;
|
||||||
*/
|
this._titleSrv = new Title();
|
||||||
constructor() {
|
this._title = '';
|
||||||
this._title = new Title();
|
|
||||||
this._disTime = 0;
|
this._disTime = 0;
|
||||||
this._trnsTime = 0;
|
this._trnsTime = 0;
|
||||||
|
|
||||||
@ -28,11 +26,12 @@ export class IonicApp {
|
|||||||
* @param {string} val Value to set the document title to.
|
* @param {string} val Value to set the document title to.
|
||||||
*/
|
*/
|
||||||
setTitle(val) {
|
setTitle(val) {
|
||||||
this._title.setTitle(val);
|
if (val !== this._title) {
|
||||||
}
|
this._title = val;
|
||||||
|
this._fastdom.defer(4, () => {
|
||||||
getTitle() {
|
this._titleSrv.setTitle(this._title);
|
||||||
return this._title.getTitle(val);
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -436,7 +436,7 @@ export class NavController extends Ion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!opts.animation) {
|
if (!opts.animation) {
|
||||||
opts.animation = this.config.get('viewTransition');
|
opts.animation = this.config.get('pageTransition');
|
||||||
}
|
}
|
||||||
if (this.config.get('animate') === false) {
|
if (this.config.get('animate') === false) {
|
||||||
opts.animate = false;
|
opts.animate = false;
|
||||||
@ -468,6 +468,7 @@ export class NavController extends Ion {
|
|||||||
leavingView.state = STAGED_LEAVING_STATE;
|
leavingView.state = STAGED_LEAVING_STATE;
|
||||||
|
|
||||||
// init the transition animation
|
// init the transition animation
|
||||||
|
opts.renderDelay = this.config.get('pageTransitionDelay');
|
||||||
let transAnimation = Transition.create(this, opts);
|
let transAnimation = Transition.create(this, opts);
|
||||||
if (opts.animate === false) {
|
if (opts.animate === false) {
|
||||||
// force it to not animate the elements, just apply the "to" styles
|
// force it to not animate the elements, just apply the "to" styles
|
||||||
|
@ -4,6 +4,7 @@ import {Ion} from '../ion';
|
|||||||
import {IonicApp} from '../app/app';
|
import {IonicApp} from '../app/app';
|
||||||
import {Attr} from '../app/id';
|
import {Attr} from '../app/id';
|
||||||
import {Config} from '../../config/config';
|
import {Config} from '../../config/config';
|
||||||
|
import {Platform} from '../../platform/platform';
|
||||||
import {ViewController} from '../nav/view-controller';
|
import {ViewController} from '../nav/view-controller';
|
||||||
import {ConfigComponent} from '../../config/decorators';
|
import {ConfigComponent} from '../../config/decorators';
|
||||||
import {Icon} from '../icon/icon';
|
import {Icon} from '../icon/icon';
|
||||||
@ -107,7 +108,8 @@ export class Tabs extends Ion {
|
|||||||
app: IonicApp,
|
app: IonicApp,
|
||||||
config: Config,
|
config: Config,
|
||||||
elementRef: ElementRef,
|
elementRef: ElementRef,
|
||||||
@Optional() viewCtrl: ViewController
|
@Optional() viewCtrl: ViewController,
|
||||||
|
private platform: Platform
|
||||||
) {
|
) {
|
||||||
super(elementRef, config);
|
super(elementRef, config);
|
||||||
this.app = app;
|
this.app = app;
|
||||||
@ -132,6 +134,15 @@ export class Tabs extends Ion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onInit() {
|
||||||
|
super.onInit();
|
||||||
|
if (this.highlight) {
|
||||||
|
this.platform.onResize(() => {
|
||||||
|
this.highlight.select(this.getSelected());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -305,7 +316,7 @@ class TabButton extends Ion {
|
|||||||
})
|
})
|
||||||
class TabHighlight {
|
class TabHighlight {
|
||||||
constructor(@Host() tabs: Tabs, config: Config, elementRef: ElementRef) {
|
constructor(@Host() tabs: Tabs, config: Config, elementRef: ElementRef) {
|
||||||
if (config.get('mode') === 'md') {
|
if (config.get('tabbarHighlight')) {
|
||||||
tabs.highlight = this;
|
tabs.highlight = this;
|
||||||
this.elementRef = elementRef;
|
this.elementRef = elementRef;
|
||||||
}
|
}
|
||||||
@ -320,7 +331,7 @@ class TabHighlight {
|
|||||||
this.init = true;
|
this.init = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
ele.classList.add('animate');
|
ele.classList.add('animate');
|
||||||
}, 64)
|
}, 64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,11 +3,12 @@ import {raf} from '../../util/dom';
|
|||||||
|
|
||||||
export class Activator {
|
export class Activator {
|
||||||
|
|
||||||
constructor(app, config) {
|
constructor(app, config, fastdom) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
this.fastdom = fastdom;
|
||||||
this.queue = [];
|
this.queue = [];
|
||||||
this.active = [];
|
this.active = [];
|
||||||
this.clearStateTimeout = 80;
|
this.clearStateDefers = 5;
|
||||||
this.clearAttempt = 0;
|
this.clearAttempt = 0;
|
||||||
this.activatedClass = config.get('activatedClass') || 'activated';
|
this.activatedClass = config.get('activatedClass') || 'activated';
|
||||||
this.x = 0;
|
this.x = 0;
|
||||||
@ -17,7 +18,7 @@ export class Activator {
|
|||||||
downAction(ev, activatableEle, pointerX, pointerY, callback) {
|
downAction(ev, activatableEle, pointerX, pointerY, callback) {
|
||||||
// the user just pressed down
|
// the user just pressed down
|
||||||
|
|
||||||
if (this.disableActivated(ev)) return;
|
if (this.disableActivated(ev)) return false;
|
||||||
|
|
||||||
// remember where they pressed
|
// remember where they pressed
|
||||||
this.x = pointerX;
|
this.x = pointerX;
|
||||||
@ -26,7 +27,7 @@ export class Activator {
|
|||||||
// queue to have this element activated
|
// queue to have this element activated
|
||||||
this.queue.push(activatableEle);
|
this.queue.push(activatableEle);
|
||||||
|
|
||||||
raf(() => {
|
this.fastdom.write(() => {
|
||||||
let activatableEle;
|
let activatableEle;
|
||||||
for (let i = 0; i < this.queue.length; i++) {
|
for (let i = 0; i < this.queue.length; i++) {
|
||||||
activatableEle = this.queue[i];
|
activatableEle = this.queue[i];
|
||||||
@ -37,13 +38,15 @@ export class Activator {
|
|||||||
}
|
}
|
||||||
this.queue = [];
|
this.queue = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
upAction() {
|
upAction() {
|
||||||
// the user was pressing down, then just let up
|
// the user was pressing down, then just let up
|
||||||
setTimeout(() => {
|
this.fastdom.defer(this.clearStateDefers, () => {
|
||||||
this.clearState();
|
this.clearState();
|
||||||
}, this.clearStateTimeout);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
clearState() {
|
clearState() {
|
||||||
@ -65,11 +68,14 @@ export class Activator {
|
|||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
// remove the active class from all active elements
|
// remove the active class from all active elements
|
||||||
for (let i = 0; i < this.active.length; i++) {
|
|
||||||
this.active[i].classList.remove(this.activatedClass);
|
|
||||||
}
|
|
||||||
this.queue = [];
|
this.queue = [];
|
||||||
this.active = [];
|
|
||||||
|
this.fastdom.write(() => {
|
||||||
|
for (let i = 0; i < this.active.length; i++) {
|
||||||
|
this.active[i].classList.remove(this.activatedClass);
|
||||||
|
}
|
||||||
|
this.active = [];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
disableActivated(ev) {
|
disableActivated(ev) {
|
||||||
|
@ -1,23 +1,38 @@
|
|||||||
import {Activator} from './activator';
|
import {Activator} from './activator';
|
||||||
import {removeElement, raf} from '../../util/dom';
|
|
||||||
import {Animation} from '../../animations/animation';
|
import {Animation} from '../../animations/animation';
|
||||||
|
import {raf} from '../../util/dom';
|
||||||
|
|
||||||
|
|
||||||
export class RippleActivator extends Activator {
|
export class RippleActivator extends Activator {
|
||||||
|
|
||||||
constructor(app, config) {
|
constructor(app, config, fastdom) {
|
||||||
super(app, config);
|
super(app, config, fastdom);
|
||||||
this.ripples = {};
|
|
||||||
|
this.expands = {};
|
||||||
|
this.fades = {};
|
||||||
|
this.expandSpeed = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
downAction(ev, activatableEle, pointerX, pointerY) {
|
downAction(ev, activatableEle, pointerX, pointerY) {
|
||||||
|
if (super.downAction(ev, activatableEle, pointerX, pointerY) ) {
|
||||||
|
// create a new ripple element
|
||||||
|
this.expandSpeed = EXPAND_DOWN_PLAYBACK_RATE;
|
||||||
|
|
||||||
if (this.disableActivated(ev)) return;
|
this.fastdom.defer(2, () => {
|
||||||
|
|
||||||
super.downAction(ev, activatableEle, pointerX, pointerY);
|
this.fastdom.read(() => {
|
||||||
|
let clientRect = activatableEle.getBoundingClientRect();
|
||||||
|
|
||||||
// create a new ripple element
|
this.fastdom.write(() => {
|
||||||
let clientRect = activatableEle.getBoundingClientRect();
|
this.createRipple(activatableEle, pointerX, pointerY, clientRect);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createRipple(activatableEle, pointerX, pointerY, clientRect) {
|
||||||
let clientPointerX = (pointerX - clientRect.left);
|
let clientPointerX = (pointerX - clientRect.left);
|
||||||
let clientPointerY = (pointerY - clientRect.top);
|
let clientPointerY = (pointerY - clientRect.top);
|
||||||
|
|
||||||
@ -29,6 +44,7 @@ export class RippleActivator extends Activator {
|
|||||||
let duration = (1000 * Math.sqrt(radius / TOUCH_DOWN_ACCEL) + 0.5);
|
let duration = (1000 * Math.sqrt(radius / TOUCH_DOWN_ACCEL) + 0.5);
|
||||||
|
|
||||||
let rippleEle = document.createElement('md-ripple');
|
let rippleEle = document.createElement('md-ripple');
|
||||||
|
let rippleId = Date.now();
|
||||||
let eleStyle = rippleEle.style;
|
let eleStyle = rippleEle.style;
|
||||||
eleStyle.width = eleStyle.height = diameter + 'px';
|
eleStyle.width = eleStyle.height = diameter + 'px';
|
||||||
eleStyle.marginTop = eleStyle.marginLeft = -(diameter / 2) + 'px';
|
eleStyle.marginTop = eleStyle.marginLeft = -(diameter / 2) + 'px';
|
||||||
@ -37,96 +53,74 @@ export class RippleActivator extends Activator {
|
|||||||
|
|
||||||
activatableEle.appendChild(rippleEle);
|
activatableEle.appendChild(rippleEle);
|
||||||
|
|
||||||
let ripple = this.ripples[Date.now()] = {
|
// create the animation for the fade out, but don't start it yet
|
||||||
ele: rippleEle,
|
this.fades[rippleId] = new Animation(rippleEle, {renderDelay: 0});
|
||||||
radius: radius,
|
this.fades[rippleId]
|
||||||
duration: duration
|
.fadeOut()
|
||||||
};
|
.duration(FADE_OUT_DURATION)
|
||||||
|
.playbackRate(1)
|
||||||
|
.onFinish(() => {
|
||||||
|
this.fastdom.write(() => {
|
||||||
|
this.fades[rippleId].dispose(true);
|
||||||
|
delete this.fades[rippleId];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// expand the circle from the users starting point
|
// expand the circle from the users starting point
|
||||||
// start slow, and when they let up, then speed up the animation
|
// start slow, and when they let up, then speed up the animation
|
||||||
ripple.expand = new Animation(rippleEle, {renderDelay: 0});
|
this.expands[rippleId] = new Animation(rippleEle, {renderDelay: 0});
|
||||||
ripple.expand
|
this.expands[rippleId]
|
||||||
.fromTo('scale', '0.001', '1')
|
.fromTo('scale', '0.001', '1')
|
||||||
.duration(duration)
|
.duration(duration)
|
||||||
.playbackRate(EXPAND_DOWN_PLAYBACK_RATE)
|
.playbackRate(this.expandSpeed)
|
||||||
.onFinish(()=> {
|
.onFinish(()=> {
|
||||||
// finished expanding
|
this.expands[rippleId].dispose();
|
||||||
ripple.expand && ripple.expand.dispose();
|
delete this.expands[rippleId];
|
||||||
ripple.expand = null;
|
|
||||||
ripple.expanded = true;
|
|
||||||
this.next();
|
this.next();
|
||||||
})
|
})
|
||||||
.play();
|
.play();
|
||||||
|
|
||||||
this.next();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
upAction(forceFadeOut) {
|
upAction() {
|
||||||
this.deactivate();
|
this.deactivate();
|
||||||
|
|
||||||
let rippleId, ripple;
|
this.expandSpeed = 1;
|
||||||
for (rippleId in this.ripples) {
|
|
||||||
ripple = this.ripples[rippleId];
|
|
||||||
|
|
||||||
if (!ripple.fade || forceFadeOut) {
|
this.fastdom.defer(4, () => {
|
||||||
// ripple has not been let up yet
|
this.next();
|
||||||
clearTimeout(ripple.fadeStart);
|
});
|
||||||
ripple.fadeStart = setTimeout(() => {
|
}
|
||||||
// speed up the rate if the animation is still going
|
|
||||||
ripple.expand && ripple.expand.playbackRate(EXPAND_OUT_PLAYBACK_RATE);
|
|
||||||
ripple.fade = new Animation(ripple.ele);
|
|
||||||
ripple.fade
|
|
||||||
.fadeOut()
|
|
||||||
.duration(OPACITY_OUT_DURATION)
|
|
||||||
.playbackRate(1)
|
|
||||||
.onFinish(() => {
|
|
||||||
ripple.fade && ripple.fade.dispose();
|
|
||||||
ripple.fade = null;
|
|
||||||
ripple.faded = true;
|
|
||||||
this.next();
|
|
||||||
})
|
|
||||||
.play();
|
|
||||||
|
|
||||||
});
|
next() {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
let rippleId;
|
||||||
|
for (rippleId in this.expands) {
|
||||||
|
if (parseInt(rippleId, 10) + 4000 < now) {
|
||||||
|
this.expands[rippleId].dispose(true);
|
||||||
|
delete this.expands[rippleId];
|
||||||
|
|
||||||
|
} else if (this.expands[rippleId].playbackRate() === EXPAND_DOWN_PLAYBACK_RATE) {
|
||||||
|
this.expands[rippleId].playbackRate(EXPAND_OUT_PLAYBACK_RATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.next();
|
for (rippleId in this.fades) {
|
||||||
}
|
if (parseInt(rippleId, 10) + 4000 < now) {
|
||||||
|
this.fades[rippleId].dispose(true);
|
||||||
|
delete this.fades[rippleId];
|
||||||
|
|
||||||
next(forceComplete) {
|
} else if (!this.fades[rippleId].isPlaying) {
|
||||||
let rippleId, ripple;
|
this.fades[rippleId].isPlaying = true;
|
||||||
for (rippleId in this.ripples) {
|
this.fades[rippleId].play();
|
||||||
ripple = this.ripples[rippleId];
|
|
||||||
|
|
||||||
if ((ripple.expanded && ripple.faded && ripple.ele) || forceComplete) {
|
|
||||||
// finished expanding and the user has lifted the pointer
|
|
||||||
ripple.remove = true;
|
|
||||||
raf(() => {
|
|
||||||
this.remove();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearState() {
|
clearState() {
|
||||||
this.deactivate();
|
this.deactivate();
|
||||||
this.next(true);
|
this.next();
|
||||||
}
|
|
||||||
|
|
||||||
remove() {
|
|
||||||
let rippleId, ripple;
|
|
||||||
for (rippleId in this.ripples) {
|
|
||||||
ripple = this.ripples[rippleId];
|
|
||||||
if (ripple.remove || parseInt(rippleId, 10) + 4000 < Date.now()) {
|
|
||||||
ripple.expand && ripple.expand.dispose();
|
|
||||||
ripple.fade && ripple.fade.dispose();
|
|
||||||
removeElement(ripple.ele);
|
|
||||||
ripple.ele = ripple.expand = ripple.fade = null;
|
|
||||||
delete this.ripples[rippleId];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -134,4 +128,4 @@ export class RippleActivator extends Activator {
|
|||||||
const TOUCH_DOWN_ACCEL = 512;
|
const TOUCH_DOWN_ACCEL = 512;
|
||||||
const EXPAND_DOWN_PLAYBACK_RATE = 0.35;
|
const EXPAND_DOWN_PLAYBACK_RATE = 0.35;
|
||||||
const EXPAND_OUT_PLAYBACK_RATE = 3;
|
const EXPAND_OUT_PLAYBACK_RATE = 3;
|
||||||
const OPACITY_OUT_DURATION = 750;
|
const FADE_OUT_DURATION = 700;
|
||||||
|
@ -12,27 +12,29 @@ let disableNativeClickAmount = 3000;
|
|||||||
let activator = null;
|
let activator = null;
|
||||||
let isTapPolyfill = false;
|
let isTapPolyfill = false;
|
||||||
let app = null;
|
let app = null;
|
||||||
let config = null;
|
|
||||||
let win = null;
|
let win = null;
|
||||||
let doc = null;
|
let doc = null;
|
||||||
|
|
||||||
|
|
||||||
export function initTapClick(windowInstance, documentInstance, appInstance, configInstance) {
|
export function initTapClick(windowInstance, documentInstance, appInstance, config, fastdom) {
|
||||||
win = windowInstance;
|
win = windowInstance;
|
||||||
doc = documentInstance;
|
doc = documentInstance;
|
||||||
app = appInstance;
|
app = appInstance;
|
||||||
config = configInstance;
|
|
||||||
|
|
||||||
activator = (config.get('mdRipple') ? new RippleActivator(app, config) : new Activator(app, config));
|
if (config.get('activator') == 'ripple') {
|
||||||
|
activator = new RippleActivator(app, config, fastdom);
|
||||||
|
|
||||||
|
} else if (config.get('activator') == 'highlight') {
|
||||||
|
activator = new Activator(app, config, fastdom));
|
||||||
|
}
|
||||||
|
|
||||||
isTapPolyfill = (config.get('tapPolyfill') === true);
|
isTapPolyfill = (config.get('tapPolyfill') === true);
|
||||||
|
|
||||||
addListener('click', click, true);
|
addListener('click', click, true);
|
||||||
|
|
||||||
if (isTapPolyfill) {
|
addListener('touchstart', touchStart);
|
||||||
addListener('touchstart', touchStart);
|
addListener('touchend', touchEnd);
|
||||||
addListener('touchend', touchEnd);
|
addListener('touchcancel', touchCancel);
|
||||||
addListener('touchcancel', touchCancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
addListener('mousedown', mouseDown, true);
|
addListener('mousedown', mouseDown, true);
|
||||||
addListener('mouseup', mouseUp, true);
|
addListener('mouseup', mouseUp, true);
|
||||||
@ -47,7 +49,7 @@ function touchStart(ev) {
|
|||||||
function touchEnd(ev) {
|
function touchEnd(ev) {
|
||||||
touchAction();
|
touchAction();
|
||||||
|
|
||||||
if (startCoord && app.isEnabled()) {
|
if (isTapPolyfill && startCoord && app.isEnabled()) {
|
||||||
let endCoord = pointerCoord(ev);
|
let endCoord = pointerCoord(ev);
|
||||||
|
|
||||||
if (!hasPointerMoved(pointerTolerance, startCoord, endCoord)) {
|
if (!hasPointerMoved(pointerTolerance, startCoord, endCoord)) {
|
||||||
@ -100,8 +102,8 @@ function pointerStart(ev) {
|
|||||||
startCoord = pointerCoord(ev);
|
startCoord = pointerCoord(ev);
|
||||||
|
|
||||||
let now = Date.now();
|
let now = Date.now();
|
||||||
if (lastActivated + 100 < now) {
|
if (lastActivated + 150 < now) {
|
||||||
activator.downAction(ev, activatableEle, startCoord.x, startCoord.y);
|
activator && activator.downAction(ev, activatableEle, startCoord.x, startCoord.y);
|
||||||
lastActivated = now;
|
lastActivated = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +116,7 @@ function pointerStart(ev) {
|
|||||||
|
|
||||||
function pointerEnd(ev) {
|
function pointerEnd(ev) {
|
||||||
moveListeners(false);
|
moveListeners(false);
|
||||||
activator.upAction();
|
activator && activator.upAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
function pointerMove(ev) {
|
function pointerMove(ev) {
|
||||||
@ -127,20 +129,22 @@ function pointerMove(ev) {
|
|||||||
|
|
||||||
function pointerCancel(ev) {
|
function pointerCancel(ev) {
|
||||||
console.debug('pointerCancel from', ev.type);
|
console.debug('pointerCancel from', ev.type);
|
||||||
activator.clearState();
|
activator && activator.clearState();
|
||||||
moveListeners(false);
|
moveListeners(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveListeners(shouldAdd) {
|
function moveListeners(shouldAdd) {
|
||||||
if (isTapPolyfill) {
|
|
||||||
removeListener('touchmove', pointerMove);
|
|
||||||
}
|
|
||||||
removeListener('mousemove', pointerMove);
|
|
||||||
if (shouldAdd) {
|
if (shouldAdd) {
|
||||||
if (isTapPolyfill) {
|
if (isTapPolyfill) {
|
||||||
addListener('touchmove', pointerMove);
|
addListener('touchmove', pointerMove);
|
||||||
}
|
}
|
||||||
addListener('mousemove', pointerMove);
|
addListener('mousemove', pointerMove);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (isTapPolyfill) {
|
||||||
|
removeListener('touchmove', pointerMove);
|
||||||
|
}
|
||||||
|
removeListener('mousemove', pointerMove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,8 +172,6 @@ function click(ev) {
|
|||||||
console.debug('click prevent', preventReason);
|
console.debug('click prevent', preventReason);
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
} else {
|
|
||||||
activator.upAction();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import {IonicApp} from '../components/app/app';
|
|||||||
import {Config} from './config';
|
import {Config} from './config';
|
||||||
import {Platform} from '../platform/platform';
|
import {Platform} from '../platform/platform';
|
||||||
import {OverlayController} from '../components/overlay/overlay-controller';
|
import {OverlayController} from '../components/overlay/overlay-controller';
|
||||||
|
import {FastDom} from '../util/fastdom';
|
||||||
import {Form} from '../util/form';
|
import {Form} from '../util/form';
|
||||||
import {Keyboard} from '../util/keyboard';
|
import {Keyboard} from '../util/keyboard';
|
||||||
import {ActionSheet} from '../components/action-sheet/action-sheet';
|
import {ActionSheet} from '../components/action-sheet/action-sheet';
|
||||||
@ -21,7 +22,9 @@ import * as dom from '../util/dom';
|
|||||||
|
|
||||||
|
|
||||||
export function ionicProviders(args) {
|
export function ionicProviders(args) {
|
||||||
let app = new IonicApp();
|
let fastdom = new FastDom();
|
||||||
|
|
||||||
|
let app = new IonicApp(fastdom);
|
||||||
let platform = new Platform();
|
let platform = new Platform();
|
||||||
let navRegistry = new NavRegistry(args.pages);
|
let navRegistry = new NavRegistry(args.pages);
|
||||||
|
|
||||||
@ -38,7 +41,7 @@ export function ionicProviders(args) {
|
|||||||
config.setPlatform(platform);
|
config.setPlatform(platform);
|
||||||
|
|
||||||
let events = new Events();
|
let events = new Events();
|
||||||
initTapClick(window, document, app, config);
|
initTapClick(window, document, app, config, fastdom);
|
||||||
let featureDetect = new FeatureDetect();
|
let featureDetect = new FeatureDetect();
|
||||||
|
|
||||||
setupDom(window, document, config, platform, featureDetect);
|
setupDom(window, document, config, platform, featureDetect);
|
||||||
@ -48,6 +51,7 @@ export function ionicProviders(args) {
|
|||||||
platform.prepareReady(config);
|
platform.prepareReady(config);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
provide(FastDom, {useValue: fastdom}),
|
||||||
provide(IonicApp, {useValue: app}),
|
provide(IonicApp, {useValue: app}),
|
||||||
provide(Config, {useValue: config}),
|
provide(Config, {useValue: config}),
|
||||||
provide(Platform, {useValue: platform}),
|
provide(Platform, {useValue: platform}),
|
||||||
|
@ -22,7 +22,7 @@ import {isObject, isDefined, isFunction, isArray, extend} from '../util/util';
|
|||||||
* modalEnter: 'modal-slide-in',
|
* modalEnter: 'modal-slide-in',
|
||||||
* modalLeave: 'modal-slide-out',
|
* modalLeave: 'modal-slide-out',
|
||||||
* tabbarPlacement: 'bottom',
|
* tabbarPlacement: 'bottom',
|
||||||
* viewTransition: 'ios',
|
* pageTransition: 'ios',
|
||||||
* }
|
* }
|
||||||
* })
|
* })
|
||||||
* ```
|
* ```
|
||||||
|
@ -4,6 +4,7 @@ import {Config} from './config';
|
|||||||
|
|
||||||
// iOS Mode Settings
|
// iOS Mode Settings
|
||||||
Config.setModeConfig('ios', {
|
Config.setModeConfig('ios', {
|
||||||
|
activator: 'highlight',
|
||||||
|
|
||||||
actionSheetEnter: 'action-sheet-slide-in',
|
actionSheetEnter: 'action-sheet-slide-in',
|
||||||
actionSheetLeave: 'action-sheet-slide-out',
|
actionSheetLeave: 'action-sheet-slide-out',
|
||||||
@ -18,16 +19,19 @@ Config.setModeConfig('ios', {
|
|||||||
modalEnter: 'modal-slide-in',
|
modalEnter: 'modal-slide-in',
|
||||||
modalLeave: 'modal-slide-out',
|
modalLeave: 'modal-slide-out',
|
||||||
|
|
||||||
tabbarPlacement: 'bottom',
|
pageTransition: 'ios',
|
||||||
viewTransition: 'ios',
|
pageTransitionDelay: 16,
|
||||||
|
|
||||||
popupPopIn: 'popup-pop-in',
|
popupPopIn: 'popup-pop-in',
|
||||||
popupPopOut: 'popup-pop-out',
|
popupPopOut: 'popup-pop-out',
|
||||||
|
|
||||||
|
tabbarPlacement: 'bottom',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Material Design Mode Settings
|
// Material Design Mode Settings
|
||||||
Config.setModeConfig('md', {
|
Config.setModeConfig('md', {
|
||||||
|
activator: 'ripple',
|
||||||
|
|
||||||
actionSheetEnter: 'action-sheet-md-slide-in',
|
actionSheetEnter: 'action-sheet-md-slide-in',
|
||||||
actionSheetLeave: 'action-sheet-md-slide-out',
|
actionSheetLeave: 'action-sheet-md-slide-out',
|
||||||
@ -39,16 +43,19 @@ Config.setModeConfig('md', {
|
|||||||
|
|
||||||
iconMode: 'md',
|
iconMode: 'md',
|
||||||
|
|
||||||
|
type: 'overlay',
|
||||||
|
|
||||||
modalEnter: 'modal-md-slide-in',
|
modalEnter: 'modal-md-slide-in',
|
||||||
modalLeave: 'modal-md-slide-out',
|
modalLeave: 'modal-md-slide-out',
|
||||||
|
|
||||||
tabbarPlacement: 'top',
|
pageTransition: 'md',
|
||||||
viewTransition: 'md',
|
pageTransitionDelay: 80,
|
||||||
|
|
||||||
popupPopIn: 'popup-md-pop-in',
|
popupPopIn: 'popup-md-pop-in',
|
||||||
popupPopOut: 'popup-md-pop-out',
|
popupPopOut: 'popup-md-pop-out',
|
||||||
|
|
||||||
|
tabbarHighlight: true,
|
||||||
|
tabbarPlacement: 'top',
|
||||||
|
|
||||||
tabSubPages: true,
|
tabSubPages: true,
|
||||||
type: 'overlay',
|
|
||||||
mdRipple: true,
|
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,6 @@ const SHOW_BACK_BTN_CSS = 'show-back-button';
|
|||||||
class MDTransition extends Animation {
|
class MDTransition extends Animation {
|
||||||
|
|
||||||
constructor(navCtrl, opts) {
|
constructor(navCtrl, opts) {
|
||||||
//opts.renderDelay = 80;
|
|
||||||
super(null, opts);
|
super(null, opts);
|
||||||
|
|
||||||
// what direction is the transition going
|
// what direction is the transition going
|
||||||
|
389
ionic/util/fastdom.ts
Normal file
389
ionic/util/fastdom.ts
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
import {raf} from './dom';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FastDom
|
||||||
|
*
|
||||||
|
* Eliminates layout thrashing
|
||||||
|
* by batching DOM read/write
|
||||||
|
* interactions.
|
||||||
|
*
|
||||||
|
* @author Wilson Page <wilsonpage@me.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a fresh
|
||||||
|
* FastDom instance.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
export function FastDom() {
|
||||||
|
this.frames = [];
|
||||||
|
this.lastId = 0;
|
||||||
|
|
||||||
|
// Placing the rAF method
|
||||||
|
// on the instance allows
|
||||||
|
// us to replace it with
|
||||||
|
// a stub for testing.
|
||||||
|
this.raf = raf;
|
||||||
|
|
||||||
|
this.batch = {
|
||||||
|
hash: {},
|
||||||
|
read: [],
|
||||||
|
write: [],
|
||||||
|
mode: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a job to the
|
||||||
|
* read batch and schedules
|
||||||
|
* a new frame if need be.
|
||||||
|
*
|
||||||
|
* @param {Function} fn
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
FastDom.prototype.read = function(fn, ctx) {
|
||||||
|
var job = this.add('read', fn, ctx);
|
||||||
|
var id = job.id;
|
||||||
|
|
||||||
|
// Add this job to the read queue
|
||||||
|
this.batch.read.push(job.id);
|
||||||
|
|
||||||
|
// We should *not* schedule a new frame if:
|
||||||
|
// 1. We're 'reading'
|
||||||
|
// 2. A frame is already scheduled
|
||||||
|
var doesntNeedFrame = this.batch.mode === 'reading'
|
||||||
|
|| this.batch.scheduled;
|
||||||
|
|
||||||
|
// If a frame isn't needed, return
|
||||||
|
if (doesntNeedFrame) return id;
|
||||||
|
|
||||||
|
// Schedule a new
|
||||||
|
// frame, then return
|
||||||
|
this.scheduleBatch();
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a job to the
|
||||||
|
* write batch and schedules
|
||||||
|
* a new frame if need be.
|
||||||
|
*
|
||||||
|
* @param {Function} fn
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
FastDom.prototype.write = function(fn, ctx) {
|
||||||
|
var job = this.add('write', fn, ctx);
|
||||||
|
var mode = this.batch.mode;
|
||||||
|
var id = job.id;
|
||||||
|
|
||||||
|
// Push the job id into the queue
|
||||||
|
this.batch.write.push(job.id);
|
||||||
|
|
||||||
|
// We should *not* schedule a new frame if:
|
||||||
|
// 1. We are 'writing'
|
||||||
|
// 2. We are 'reading'
|
||||||
|
// 3. A frame is already scheduled.
|
||||||
|
var doesntNeedFrame = mode === 'writing'
|
||||||
|
|| mode === 'reading'
|
||||||
|
|| this.batch.scheduled;
|
||||||
|
|
||||||
|
// If a frame isn't needed, return
|
||||||
|
if (doesntNeedFrame) return id;
|
||||||
|
|
||||||
|
// Schedule a new
|
||||||
|
// frame, then return
|
||||||
|
this.scheduleBatch();
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defers the given job
|
||||||
|
* by the number of frames
|
||||||
|
* specified.
|
||||||
|
*
|
||||||
|
* If no frames are given
|
||||||
|
* then the job is run in
|
||||||
|
* the next free frame.
|
||||||
|
*
|
||||||
|
* @param {Number} frame
|
||||||
|
* @param {Function} fn
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
FastDom.prototype.defer = function(frame, fn, ctx) {
|
||||||
|
|
||||||
|
// Accepts two arguments
|
||||||
|
if (typeof frame === 'function') {
|
||||||
|
ctx = fn;
|
||||||
|
fn = frame;
|
||||||
|
frame = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var index = frame - 1;
|
||||||
|
|
||||||
|
return this.schedule(index, function() {
|
||||||
|
self.run({
|
||||||
|
fn: fn,
|
||||||
|
ctx: ctx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears a scheduled 'read',
|
||||||
|
* 'write' or 'defer' job.
|
||||||
|
*
|
||||||
|
* @param {Number|String} id
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
FastDom.prototype.clear = function(id) {
|
||||||
|
|
||||||
|
// Defer jobs are cleared differently
|
||||||
|
if (typeof id === 'function') {
|
||||||
|
return this.clearFrame(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow ids to be passed as strings
|
||||||
|
id = Number(id);
|
||||||
|
|
||||||
|
var job = this.batch.hash[id];
|
||||||
|
if (!job) return;
|
||||||
|
|
||||||
|
var list = this.batch[job.type];
|
||||||
|
var index = list.indexOf(id);
|
||||||
|
|
||||||
|
// Clear references
|
||||||
|
delete this.batch.hash[id];
|
||||||
|
if (~index) list.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears a scheduled frame.
|
||||||
|
*
|
||||||
|
* @param {Function} frame
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
FastDom.prototype.clearFrame = function(frame) {
|
||||||
|
var index = this.frames.indexOf(frame);
|
||||||
|
if (~index) this.frames.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a new read/write
|
||||||
|
* batch if one isn't pending.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
FastDom.prototype.scheduleBatch = function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Schedule batch for next frame
|
||||||
|
this.schedule(0, function() {
|
||||||
|
self.batch.scheduled = false;
|
||||||
|
self.runBatch();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set flag to indicate
|
||||||
|
// a frame has been scheduled
|
||||||
|
this.batch.scheduled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a unique
|
||||||
|
* id for a job.
|
||||||
|
*
|
||||||
|
* @return {Number}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
FastDom.prototype.uniqueId = function() {
|
||||||
|
return ++this.lastId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls each job in
|
||||||
|
* the list passed.
|
||||||
|
*
|
||||||
|
* If a context has been
|
||||||
|
* stored on the function
|
||||||
|
* then it is used, else the
|
||||||
|
* current `this` is used.
|
||||||
|
*
|
||||||
|
* @param {Array} list
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
FastDom.prototype.flush = function(list) {
|
||||||
|
var id;
|
||||||
|
|
||||||
|
while (id = list.shift()) {
|
||||||
|
this.run(this.batch.hash[id]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs any 'read' jobs followed
|
||||||
|
* by any 'write' jobs.
|
||||||
|
*
|
||||||
|
* We run this inside a try catch
|
||||||
|
* so that if any jobs error, we
|
||||||
|
* are able to recover and continue
|
||||||
|
* to flush the batch until it's empty.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
FastDom.prototype.runBatch = function() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Set the mode to 'reading',
|
||||||
|
// then empty all read jobs
|
||||||
|
this.batch.mode = 'reading';
|
||||||
|
this.flush(this.batch.read);
|
||||||
|
|
||||||
|
// Set the mode to 'writing'
|
||||||
|
// then empty all write jobs
|
||||||
|
this.batch.mode = 'writing';
|
||||||
|
this.flush(this.batch.write);
|
||||||
|
|
||||||
|
this.batch.mode = null;
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
this.runBatch();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new job to
|
||||||
|
* the given batch.
|
||||||
|
*
|
||||||
|
* @param {Array} list
|
||||||
|
* @param {Function} fn
|
||||||
|
* @param {Object} ctx
|
||||||
|
* @returns {Number} id
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
FastDom.prototype.add = function(type, fn, ctx) {
|
||||||
|
var id = this.uniqueId();
|
||||||
|
return this.batch.hash[id] = {
|
||||||
|
id: id,
|
||||||
|
fn: fn,
|
||||||
|
ctx: ctx,
|
||||||
|
type: type
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a given job.
|
||||||
|
*
|
||||||
|
* Applications using FastDom
|
||||||
|
* have the options of setting
|
||||||
|
* `fastdom.onError`.
|
||||||
|
*
|
||||||
|
* This will catch any
|
||||||
|
* errors that may throw
|
||||||
|
* inside callbacks, which
|
||||||
|
* is useful as often DOM
|
||||||
|
* nodes have been removed
|
||||||
|
* since a job was scheduled.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* fastdom.onError = function(e) {
|
||||||
|
* // Runs when jobs error
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* @param {Object} job
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
FastDom.prototype.run = function(job){
|
||||||
|
var ctx = job.ctx || this;
|
||||||
|
var fn = job.fn;
|
||||||
|
|
||||||
|
// Clear reference to the job
|
||||||
|
delete this.batch.hash[job.id];
|
||||||
|
|
||||||
|
// If no `onError` handler
|
||||||
|
// has been registered, just
|
||||||
|
// run the job normally.
|
||||||
|
if (!this.onError) {
|
||||||
|
return fn.call(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an `onError` handler
|
||||||
|
// has been registered, catch
|
||||||
|
// errors that throw inside
|
||||||
|
// callbacks, and run the
|
||||||
|
// handler instead.
|
||||||
|
try { fn.call(ctx); } catch (e) {
|
||||||
|
this.onError(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a rAF loop
|
||||||
|
* to empty the frame queue.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
FastDom.prototype.loop = function() {
|
||||||
|
var self = this;
|
||||||
|
var raf = this.raf;
|
||||||
|
|
||||||
|
// Don't start more than one loop
|
||||||
|
if (this.looping) return;
|
||||||
|
|
||||||
|
raf(function frame() {
|
||||||
|
var fn = self.frames.shift();
|
||||||
|
|
||||||
|
// If no more frames,
|
||||||
|
// stop looping
|
||||||
|
if (!self.frames.length) {
|
||||||
|
self.looping = false;
|
||||||
|
|
||||||
|
// Otherwise, schedule the
|
||||||
|
// next frame
|
||||||
|
} else {
|
||||||
|
raf(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the frame. Note that
|
||||||
|
// this may throw an error
|
||||||
|
// in user code, but all
|
||||||
|
// fastdom tasks are dealt
|
||||||
|
// with already so the code
|
||||||
|
// will continue to iterate
|
||||||
|
if (fn) fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.looping = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a function to
|
||||||
|
* a specified index
|
||||||
|
* of the frame queue.
|
||||||
|
*
|
||||||
|
* @param {Number} index
|
||||||
|
* @param {Function} fn
|
||||||
|
* @return {Function}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
FastDom.prototype.schedule = function(index, fn) {
|
||||||
|
|
||||||
|
// Make sure this slot
|
||||||
|
// hasn't already been
|
||||||
|
// taken. If it has, try
|
||||||
|
// re-scheduling for the next slot
|
||||||
|
if (this.frames[index]) {
|
||||||
|
return this.schedule(index + 1, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the rAF
|
||||||
|
// loop to empty
|
||||||
|
// the frame queue
|
||||||
|
this.loop();
|
||||||
|
|
||||||
|
// Insert this function into
|
||||||
|
// the frames queue and return
|
||||||
|
return this.frames[index] = fn;
|
||||||
|
};
|
Reference in New Issue
Block a user