fix(content): scroll listener is auto enabled

fixes #10938
This commit is contained in:
Manuel Mtz-Almeida
2017-03-29 14:36:49 +02:00
parent 54acc74fdb
commit d9a7652912
5 changed files with 53 additions and 47 deletions

View File

@ -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;
} }
/** /**

View File

@ -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();

View File

@ -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();
} }
} }

View File

@ -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';

View File

@ -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;
} }
} }