mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-23 22:17:40 +08:00
@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, NgZone, OnDestroy, Optional, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
|
import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, NgZone, OnDestroy, Optional, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
|
|
||||||
import { App } from '../app/app';
|
import { App } from '../app/app';
|
||||||
import { Config } from '../../config/config';
|
import { Config } from '../../config/config';
|
||||||
@ -17,6 +17,14 @@ import { ViewController } from '../../navigation/view-controller';
|
|||||||
export { ScrollEvent } from '../../util/scroll-view';
|
export { ScrollEvent } from '../../util/scroll-view';
|
||||||
|
|
||||||
|
|
||||||
|
export class EventEmitterProxy<T> extends EventEmitter<T> {
|
||||||
|
onSubscribe: Function;
|
||||||
|
subscribe(generatorOrNext?: any, error?: any, complete?: any): any {
|
||||||
|
this.onSubscribe();
|
||||||
|
return super.subscribe(generatorOrNext, error, complete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name Content
|
* @name Content
|
||||||
* @description
|
* @description
|
||||||
@ -125,7 +133,7 @@ export { ScrollEvent } from '../../util/scroll-view';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class Content extends Ion implements OnDestroy {
|
export class Content extends Ion implements OnDestroy, AfterViewInit {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_cTop: number;
|
_cTop: number;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
@ -312,17 +320,17 @@ export class Content extends Ion implements OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* @output {ScrollEvent} Emitted when the scrolling first starts.
|
* @output {ScrollEvent} Emitted when the scrolling first starts.
|
||||||
*/
|
*/
|
||||||
@Output() ionScrollStart: EventEmitter<ScrollEvent> = new EventEmitter<ScrollEvent>();
|
@Output() ionScrollStart: EventEmitterProxy<ScrollEvent> = new EventEmitterProxy<ScrollEvent>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @output {ScrollEvent} Emitted on every scroll event.
|
* @output {ScrollEvent} Emitted on every scroll event.
|
||||||
*/
|
*/
|
||||||
@Output() ionScroll: EventEmitter<ScrollEvent> = new EventEmitter<ScrollEvent>();
|
@Output() ionScroll: EventEmitterProxy<ScrollEvent> = new EventEmitterProxy<ScrollEvent>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @output {ScrollEvent} Emitted when scrolling ends.
|
* @output {ScrollEvent} Emitted when scrolling ends.
|
||||||
*/
|
*/
|
||||||
@Output() ionScrollEnd: EventEmitter<ScrollEvent> = new EventEmitter<ScrollEvent>();
|
@Output() ionScrollEnd: EventEmitterProxy<ScrollEvent> = new EventEmitterProxy<ScrollEvent>();
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -339,6 +347,11 @@ export class Content extends Ion implements OnDestroy {
|
|||||||
) {
|
) {
|
||||||
super(config, elementRef, renderer, 'content');
|
super(config, elementRef, renderer, 'content');
|
||||||
|
|
||||||
|
let enableScrollListener = this.enableScrollListener.bind(this);
|
||||||
|
this.ionScroll.onSubscribe = enableScrollListener;
|
||||||
|
this.ionScrollStart.onSubscribe = enableScrollListener;
|
||||||
|
this.ionScrollEnd.onSubscribe = enableScrollListener;
|
||||||
|
|
||||||
this.statusbarPadding = config.getBoolean('statusbarPadding', false);
|
this.statusbarPadding = config.getBoolean('statusbarPadding', false);
|
||||||
this._imgReqBfr = config.getNumber('imgRequestBuffer', 1400);
|
this._imgReqBfr = config.getNumber('imgRequestBuffer', 1400);
|
||||||
this._imgRndBfr = config.getNumber('imgRenderBuffer', 400);
|
this._imgRndBfr = config.getNumber('imgRenderBuffer', 400);
|
||||||
@ -348,7 +361,8 @@ export class Content extends Ion implements OnDestroy {
|
|||||||
// goal is to completely remove this when iOS
|
// goal is to completely remove this when iOS
|
||||||
// fully supports scroll events
|
// fully supports scroll events
|
||||||
// listen to JS scroll events
|
// listen to JS scroll events
|
||||||
this._scroll = new ScrollView(_plt, _dom, config.getBoolean('virtualScrollEventAssist'));
|
const jsScroll = config.getBoolean('virtualScrollEventAssist');
|
||||||
|
this._scroll = new ScrollView(_app, _plt, _dom, jsScroll);
|
||||||
|
|
||||||
while (navCtrl) {
|
while (navCtrl) {
|
||||||
if (isTabs(<any>navCtrl)) {
|
if (isTabs(<any>navCtrl)) {
|
||||||
@ -383,7 +397,7 @@ export class Content extends Ion implements OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
enableScrollListener() {
|
ngAfterViewInit() {
|
||||||
assert(this.getFixedElement(), 'fixed element was not found');
|
assert(this.getFixedElement(), 'fixed element was not found');
|
||||||
assert(this.getScrollElement(), 'scroll element was not found');
|
assert(this.getScrollElement(), 'scroll element was not found');
|
||||||
|
|
||||||
@ -398,9 +412,6 @@ export class Content extends Ion implements OnDestroy {
|
|||||||
|
|
||||||
// subscribe to every scroll move
|
// subscribe to every scroll move
|
||||||
scroll.onScroll = (ev) => {
|
scroll.onScroll = (ev) => {
|
||||||
// remind the app that it's currently scrolling
|
|
||||||
this._app.setScrolling();
|
|
||||||
|
|
||||||
// emit to all of our other friends things be scrolling
|
// emit to all of our other friends things be scrolling
|
||||||
this.ionScroll.emit(ev);
|
this.ionScroll.emit(ev);
|
||||||
|
|
||||||
@ -413,8 +424,13 @@ export class Content extends Ion implements OnDestroy {
|
|||||||
|
|
||||||
this.imgsUpdate();
|
this.imgsUpdate();
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
scroll.setEnabled();
|
/**
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
enableScrollListener() {
|
||||||
|
this._scroll.eventsEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -371,7 +371,6 @@ export class InfiniteScroll {
|
|||||||
if (shouldListen) {
|
if (shouldListen) {
|
||||||
if (!this._scLsn) {
|
if (!this._scLsn) {
|
||||||
this._scLsn = this._content.ionScroll.subscribe(this._onScroll.bind(this));
|
this._scLsn = this._content.ionScroll.subscribe(this._onScroll.bind(this));
|
||||||
this._content.enableScrollListener();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this._scLsn && this._scLsn.unsubscribe();
|
this._scLsn && this._scLsn.unsubscribe();
|
||||||
|
@ -709,7 +709,6 @@ export class VirtualScroll implements DoCheck, AfterContentInit, OnDestroy {
|
|||||||
this._resizeSub = this._plt.resize.subscribe(this.resize.bind(this));
|
this._resizeSub = this._plt.resize.subscribe(this.resize.bind(this));
|
||||||
this._scrollSub = this._content.ionScroll.subscribe(this.scrollUpdate.bind(this));
|
this._scrollSub = this._content.ionScroll.subscribe(this.scrollUpdate.bind(this));
|
||||||
this._scrollEndSub = this._content.ionScrollEnd.subscribe(this.scrollEnd.bind(this));
|
this._scrollEndSub = this._content.ionScrollEnd.subscribe(this.scrollEnd.bind(this));
|
||||||
this._content.enableScrollListener();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,8 +137,8 @@ export function setupEvents(plt: Platform, dom: DomController): Events {
|
|||||||
let contentEle = <any>el.closest('.scroll-content');
|
let contentEle = <any>el.closest('.scroll-content');
|
||||||
if (contentEle) {
|
if (contentEle) {
|
||||||
var style = contentEle.style;
|
var style = contentEle.style;
|
||||||
var scroll = new ScrollView(plt, dom, false);
|
var scroll = new ScrollView(null, plt, dom, false);
|
||||||
scroll.init(contentEle, 0, 0);
|
scroll._el = contentEle;
|
||||||
// We need to stop scrolling if it's happening and scroll up
|
// We need to stop scrolling if it's happening and scroll up
|
||||||
|
|
||||||
style['WebkitBackfaceVisibility'] = 'hidden';
|
style['WebkitBackfaceVisibility'] = 'hidden';
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
import { assert } from './util';
|
import { assert } from './util';
|
||||||
|
import { App } from '../components/app/app';
|
||||||
import { DomController, DomCallback } from '../platform/dom-controller';
|
import { DomController, DomCallback } from '../platform/dom-controller';
|
||||||
import { Platform, EventListenerOptions } from '../platform/platform';
|
import { Platform, EventListenerOptions } from '../platform/platform';
|
||||||
import { pointerCoord } from './dom';
|
import { pointerCoord } from './dom';
|
||||||
@ -12,11 +13,11 @@ export class ScrollView {
|
|||||||
onScroll: (ev: ScrollEvent) => void;
|
onScroll: (ev: ScrollEvent) => void;
|
||||||
onScrollEnd: (ev: ScrollEvent) => void;
|
onScrollEnd: (ev: ScrollEvent) => void;
|
||||||
initialized: boolean = false;
|
initialized: boolean = false;
|
||||||
enabled: boolean = false;
|
eventsEnabled: boolean = false;
|
||||||
contentTop: number;
|
contentTop: number;
|
||||||
contentBottom: number;
|
contentBottom: number;
|
||||||
|
|
||||||
private _el: HTMLElement;
|
_el: HTMLElement;
|
||||||
private _js: boolean;
|
private _js: boolean;
|
||||||
private _t: number = 0;
|
private _t: number = 0;
|
||||||
private _l: number = 0;
|
private _l: number = 0;
|
||||||
@ -25,6 +26,7 @@ export class ScrollView {
|
|||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private _app: App,
|
||||||
private _plt: Platform,
|
private _plt: Platform,
|
||||||
private _dom: DomController,
|
private _dom: DomController,
|
||||||
virtualScrollEventAssist: boolean
|
virtualScrollEventAssist: boolean
|
||||||
@ -60,35 +62,19 @@ export class ScrollView {
|
|||||||
|
|
||||||
if (!this.initialized) {
|
if (!this.initialized) {
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
|
if (this._js) {
|
||||||
if (this.enabled) {
|
this.enableJsScroll();
|
||||||
this.enable();
|
} else {
|
||||||
|
this.enableNativeScrolling();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setEnabled() {
|
|
||||||
if (!this.enabled) {
|
|
||||||
this.enabled = true;
|
|
||||||
if (this.initialized) {
|
|
||||||
this.enable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enable() {
|
|
||||||
assert(this.initialized, 'scroll must be initialized');
|
|
||||||
assert(this.enabled, 'scroll-view must be enabled');
|
|
||||||
assert(this._el, 'scroll-view, element can not be null');
|
|
||||||
|
|
||||||
if (this._js) {
|
|
||||||
this.enableJsScroll();
|
|
||||||
} else {
|
|
||||||
this.enableNativeScrolling();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enableNativeScrolling() {
|
private enableNativeScrolling() {
|
||||||
|
assert(this.onScrollStart, 'onScrollStart is not defined');
|
||||||
|
assert(this.onScroll, 'onScroll is not defined');
|
||||||
|
assert(this.onScrollEnd, 'onScrollEnd is not defined');
|
||||||
|
|
||||||
this._js = false;
|
this._js = false;
|
||||||
if (!this._el) {
|
if (!this._el) {
|
||||||
return;
|
return;
|
||||||
@ -101,6 +87,14 @@ export class ScrollView {
|
|||||||
const positions: number[] = [];
|
const positions: number[] = [];
|
||||||
|
|
||||||
function scrollCallback(scrollEvent: UIEvent) {
|
function scrollCallback(scrollEvent: UIEvent) {
|
||||||
|
// remind the app that it's currently scrolling
|
||||||
|
self._app.setScrolling();
|
||||||
|
|
||||||
|
// if events are disabled, we do nothing
|
||||||
|
if (!self.eventsEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ev.timeStamp = scrollEvent.timeStamp;
|
ev.timeStamp = scrollEvent.timeStamp;
|
||||||
// Event.timeStamp is 0 in firefox
|
// Event.timeStamp is 0 in firefox
|
||||||
if (!ev.timeStamp) {
|
if (!ev.timeStamp) {
|
||||||
@ -151,13 +145,12 @@ export class ScrollView {
|
|||||||
|
|
||||||
if (startPos !== endPos) {
|
if (startPos !== endPos) {
|
||||||
// compute relative movement between these two points
|
// compute relative movement between these two points
|
||||||
var timeOffset = (positions[endPos] - positions[startPos]);
|
|
||||||
var movedTop = (positions[startPos - 2] - positions[endPos - 2]);
|
var movedTop = (positions[startPos - 2] - positions[endPos - 2]);
|
||||||
var movedLeft = (positions[startPos - 1] - positions[endPos - 1]);
|
var movedLeft = (positions[startPos - 1] - positions[endPos - 1]);
|
||||||
|
var factor = FRAME_MS / (positions[endPos] - positions[startPos]);
|
||||||
// based on XXms compute the movement to apply for each render step
|
// based on XXms compute the movement to apply for each render step
|
||||||
ev.velocityY = ((movedTop / timeOffset) * FRAME_MS);
|
ev.velocityY = movedTop * factor;
|
||||||
ev.velocityX = ((movedLeft / timeOffset) * FRAME_MS);
|
ev.velocityX = movedLeft * factor;
|
||||||
|
|
||||||
// figure out which direction we're scrolling
|
// figure out which direction we're scrolling
|
||||||
ev.directionY = (movedTop > 0 ? 'up' : 'down');
|
ev.directionY = (movedTop > 0 ? 'up' : 'down');
|
||||||
@ -546,11 +539,10 @@ export class ScrollView {
|
|||||||
this._endTmr && this._dom.cancel(this._endTmr);
|
this._endTmr && this._dom.cancel(this._endTmr);
|
||||||
this._lsn && this._lsn();
|
this._lsn && this._lsn();
|
||||||
|
|
||||||
this.onScrollStart = this.onScroll = this.onScrollEnd = null;
|
|
||||||
|
|
||||||
let ev = this.ev;
|
let ev = this.ev;
|
||||||
ev.domWrite = ev.contentElement = ev.fixedElement = ev.scrollElement = ev.headerElement = null;
|
ev.domWrite = ev.contentElement = ev.fixedElement = ev.scrollElement = ev.headerElement = null;
|
||||||
this._lsn = this._el = this._dom = this.ev = ev = null;
|
this._lsn = this._el = this._dom = this.ev = ev = null;
|
||||||
|
this.onScrollStart = this.onScroll = this.onScrollEnd = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user