mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-22 05:21:52 +08:00
feat(scroll): add domWrite and content elements to scroll events
This commit is contained in:
@ -7,12 +7,12 @@ import { Img } from '../img/img';
|
|||||||
import { Ion } from '../ion';
|
import { Ion } from '../ion';
|
||||||
import { isTrueProperty, assert, removeArrayItem } from '../../util/util';
|
import { isTrueProperty, assert, removeArrayItem } from '../../util/util';
|
||||||
import { Keyboard } from '../../util/keyboard';
|
import { Keyboard } from '../../util/keyboard';
|
||||||
import { ScrollView, ScrollDirection, ScrollEvent } from '../../util/scroll-view';
|
import { ScrollView, ScrollEvent } from '../../util/scroll-view';
|
||||||
import { Tabs } from '../tabs/tabs';
|
import { Tabs } from '../tabs/tabs';
|
||||||
import { transitionEnd } from '../../util/dom';
|
import { transitionEnd } from '../../util/dom';
|
||||||
import { ViewController } from '../../navigation/view-controller';
|
import { ViewController } from '../../navigation/view-controller';
|
||||||
|
|
||||||
export { ScrollEvent, ScrollDirection } from '../../util/scroll-view';
|
export { ScrollEvent } from '../../util/scroll-view';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -242,7 +242,7 @@ export class Content extends Ion implements OnDestroy, OnInit {
|
|||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
get velocityY(): number {
|
get velocityY(): number {
|
||||||
return this._scroll.ev.velocityY || 0;
|
return this._scroll.ev.velocityY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -251,24 +251,24 @@ export class Content extends Ion implements OnDestroy, OnInit {
|
|||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
get velocityX(): number {
|
get velocityX(): number {
|
||||||
return this._scroll.ev.velocityX || 0;
|
return this._scroll.ev.velocityX;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current, or last known, vertical scroll direction.
|
* The current, or last known, vertical scroll direction.
|
||||||
*
|
*
|
||||||
* @return {ScrollDirection}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
get directionY(): ScrollDirection {
|
get directionY(): string {
|
||||||
return this._scroll.ev.directionY;
|
return this._scroll.ev.directionY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current, or last known, horizontal scroll direction.
|
* The current, or last known, horizontal scroll direction.
|
||||||
*
|
*
|
||||||
* @return {ScrollDirection}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
get directionX(): ScrollDirection {
|
get directionX(): string {
|
||||||
return this._scroll.ev.directionX;
|
return this._scroll.ev.directionX;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,16 +330,18 @@ export class Content extends Ion implements OnDestroy, OnInit {
|
|||||||
const children = this._elementRef.nativeElement.children;
|
const children = this._elementRef.nativeElement.children;
|
||||||
assert(children && children.length >= 2, 'content needs at least two children');
|
assert(children && children.length >= 2, 'content needs at least two children');
|
||||||
|
|
||||||
this._fixedEle = children[0];
|
const scroll = this._scroll;
|
||||||
this._scrollEle = children[1];
|
|
||||||
|
scroll.ev.fixedElement = this._fixedEle = children[0];
|
||||||
|
scroll.ev.scrollElement = this._scrollEle = children[1];
|
||||||
|
|
||||||
// subscribe to the scroll start
|
// subscribe to the scroll start
|
||||||
this._scroll.scrollStart.subscribe(ev => {
|
scroll.scrollStart.subscribe(ev => {
|
||||||
this.ionScrollStart.emit(ev);
|
this.ionScrollStart.emit(ev);
|
||||||
});
|
});
|
||||||
|
|
||||||
// subscribe to every scroll move
|
// subscribe to every scroll move
|
||||||
this._scroll.scroll.subscribe(ev => {
|
scroll.scroll.subscribe(ev => {
|
||||||
// remind the app that it's currently scrolling
|
// remind the app that it's currently scrolling
|
||||||
this._app.setScrolling();
|
this._app.setScrolling();
|
||||||
|
|
||||||
@ -350,7 +352,7 @@ export class Content extends Ion implements OnDestroy, OnInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// subscribe to the scroll end
|
// subscribe to the scroll end
|
||||||
this._scroll.scrollEnd.subscribe(ev => {
|
scroll.scrollEnd.subscribe(ev => {
|
||||||
this.ionScrollEnd.emit(ev);
|
this.ionScrollEnd.emit(ev);
|
||||||
|
|
||||||
this.imgsUpdate();
|
this.imgsUpdate();
|
||||||
@ -576,6 +578,8 @@ export class Content extends Ion implements OnDestroy, OnInit {
|
|||||||
this._fTop = 0;
|
this._fTop = 0;
|
||||||
this._fBottom = 0;
|
this._fBottom = 0;
|
||||||
|
|
||||||
|
const scrollEvent = this._scroll.ev;
|
||||||
|
|
||||||
let ele: HTMLElement = this._elementRef.nativeElement;
|
let ele: HTMLElement = this._elementRef.nativeElement;
|
||||||
if (!ele) {
|
if (!ele) {
|
||||||
assert(false, 'ele should be valid');
|
assert(false, 'ele should be valid');
|
||||||
@ -590,6 +594,8 @@ export class Content extends Ion implements OnDestroy, OnInit {
|
|||||||
ele = <HTMLElement>children[i];
|
ele = <HTMLElement>children[i];
|
||||||
tagName = ele.tagName;
|
tagName = ele.tagName;
|
||||||
if (tagName === 'ION-CONTENT') {
|
if (tagName === 'ION-CONTENT') {
|
||||||
|
scrollEvent.contentElement = ele;
|
||||||
|
|
||||||
// ******** DOM READ ****************
|
// ******** DOM READ ****************
|
||||||
this.scrollWidth = ele.scrollWidth;
|
this.scrollWidth = ele.scrollWidth;
|
||||||
// ******** DOM READ ****************
|
// ******** DOM READ ****************
|
||||||
@ -605,10 +611,14 @@ export class Content extends Ion implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (tagName === 'ION-HEADER') {
|
} else if (tagName === 'ION-HEADER') {
|
||||||
|
scrollEvent.headerElement = ele;
|
||||||
|
|
||||||
// ******** DOM READ ****************
|
// ******** DOM READ ****************
|
||||||
this._hdrHeight = ele.clientHeight;
|
this._hdrHeight = ele.clientHeight;
|
||||||
|
|
||||||
} else if (tagName === 'ION-FOOTER') {
|
} else if (tagName === 'ION-FOOTER') {
|
||||||
|
scrollEvent.footerElement = ele;
|
||||||
|
|
||||||
// ******** DOM READ ****************
|
// ******** DOM READ ****************
|
||||||
this._ftrHeight = ele.clientHeight;
|
this._ftrHeight = ele.clientHeight;
|
||||||
this._footerEle = ele;
|
this._footerEle = ele;
|
||||||
@ -793,7 +803,7 @@ export class Content extends Ion implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateImgs(imgs: Img[], scrollTop: number, scrollHeight: number, scrollDirectionY: ScrollDirection, requestableBuffer: number, renderableBuffer: number) {
|
export function updateImgs(imgs: Img[], scrollTop: number, scrollHeight: number, scrollDirectionY: string, requestableBuffer: number, renderableBuffer: number) {
|
||||||
// ok, so it's time to see which images, if any, should be requested and rendered
|
// ok, so it's time to see which images, if any, should be requested and rendered
|
||||||
// ultimately, if we're scrolling fast then don't bother requesting or rendering
|
// ultimately, if we're scrolling fast then don't bother requesting or rendering
|
||||||
// when scrolling is done, then it needs to do a check to see which images are
|
// when scrolling is done, then it needs to do a check to see which images are
|
||||||
@ -809,7 +819,7 @@ export function updateImgs(imgs: Img[], scrollTop: number, scrollHeight: number,
|
|||||||
for (var i = 0, ilen = imgs.length; i < ilen; i++) {
|
for (var i = 0, ilen = imgs.length; i < ilen; i++) {
|
||||||
img = imgs[i];
|
img = imgs[i];
|
||||||
|
|
||||||
if (scrollDirectionY === ScrollDirection.Up) {
|
if (scrollDirectionY === 'up') {
|
||||||
// scrolling up
|
// scrolling up
|
||||||
if (img.top < scrollBottom && img.bottom > scrollTop - renderableBuffer) {
|
if (img.top < scrollBottom && img.bottom > scrollTop - renderableBuffer) {
|
||||||
// scrolling up, img is within viewable area
|
// scrolling up, img is within viewable area
|
||||||
@ -870,7 +880,7 @@ export function updateImgs(imgs: Img[], scrollTop: number, scrollHeight: number,
|
|||||||
// update all imgs which are viewable
|
// update all imgs which are viewable
|
||||||
priority1.sort(sortTopToBottom).forEach(i => i.update());
|
priority1.sort(sortTopToBottom).forEach(i => i.update());
|
||||||
|
|
||||||
if (scrollDirectionY === ScrollDirection.Up) {
|
if (scrollDirectionY === 'up') {
|
||||||
// scrolling up
|
// scrolling up
|
||||||
priority2.sort(sortTopToBottom).reverse().forEach(i => i.update());
|
priority2.sort(sortTopToBottom).reverse().forEach(i => i.update());
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ describe('Infinite Scroll', () => {
|
|||||||
let content: Content;
|
let content: Content;
|
||||||
let contentElementRef;
|
let contentElementRef;
|
||||||
let infiniteElementRef;
|
let infiniteElementRef;
|
||||||
let ev: ScrollEvent = {};
|
let ev: ScrollEvent = (<any>{});
|
||||||
let dom: MockDomController;
|
let dom: MockDomController;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -84,7 +84,7 @@ export { Card, CardContent, CardHeader, CardTitle } from './components/card/card
|
|||||||
export { Checkbox } from './components/checkbox/checkbox';
|
export { Checkbox } from './components/checkbox/checkbox';
|
||||||
export { Chip } from './components/chip/chip';
|
export { Chip } from './components/chip/chip';
|
||||||
export { ClickBlock } from './util/click-block';
|
export { ClickBlock } from './util/click-block';
|
||||||
export { Content, ScrollEvent, ScrollDirection } from './components/content/content';
|
export { Content, ScrollEvent } from './components/content/content';
|
||||||
export { DateTime } from './components/datetime/datetime';
|
export { DateTime } from './components/datetime/datetime';
|
||||||
export { FabContainer, FabButton, FabList } from './components/fab/fab';
|
export { FabContainer, FabButton, FabList } from './components/fab/fab';
|
||||||
export { Grid, Row, Col } from './components/grid/grid';
|
export { Grid, Row, Col } from './components/grid/grid';
|
||||||
|
@ -53,7 +53,7 @@ import { ToastCmp } from './components/toast/toast-component';
|
|||||||
* Export Providers
|
* Export Providers
|
||||||
*/
|
*/
|
||||||
export { Config, setupConfig, ConfigToken } from './config/config';
|
export { Config, setupConfig, ConfigToken } from './config/config';
|
||||||
export { DomController } from './util/dom-controller';
|
export { DomController, DomCallback } from './util/dom-controller';
|
||||||
export { Platform, setupPlatform, UserAgentToken, DocumentDirToken, DocLangToken, NavigatorPlatformToken } from './platform/platform';
|
export { Platform, setupPlatform, UserAgentToken, DocumentDirToken, DocLangToken, NavigatorPlatformToken } from './platform/platform';
|
||||||
export { Haptic } from './util/haptic';
|
export { Haptic } from './util/haptic';
|
||||||
export { ImgLoader } from './components/img/img-loader';
|
export { ImgLoader } from './components/img/img-loader';
|
||||||
|
@ -7,7 +7,7 @@ import { nativeRaf } from './dom';
|
|||||||
import { removeArrayItem } from './util';
|
import { removeArrayItem } from './util';
|
||||||
|
|
||||||
|
|
||||||
export type DomCallback = { (timeStamp: number): void };
|
export type DomCallback = { (timeStamp?: number): void };
|
||||||
|
|
||||||
export class DomDebouncer {
|
export class DomDebouncer {
|
||||||
|
|
||||||
|
@ -2,11 +2,12 @@ import { Subject } from 'rxjs/Subject';
|
|||||||
|
|
||||||
import { assert } from './util';
|
import { assert } from './util';
|
||||||
import { CSS, nativeRaf, pointerCoord, rafFrames } from './dom';
|
import { CSS, nativeRaf, pointerCoord, rafFrames } from './dom';
|
||||||
import { DomController } from './dom-controller';
|
import { DomController, DomCallback } from './dom-controller';
|
||||||
import { eventOptions, listenEvent } from './ui-event-manager';
|
import { eventOptions, listenEvent } from './ui-event-manager';
|
||||||
|
|
||||||
|
|
||||||
export class ScrollView {
|
export class ScrollView {
|
||||||
|
ev: ScrollEvent;
|
||||||
isScrolling = false;
|
isScrolling = false;
|
||||||
scrollStart = new Subject<ScrollEvent>();
|
scrollStart = new Subject<ScrollEvent>();
|
||||||
scroll = new Subject<ScrollEvent>();
|
scroll = new Subject<ScrollEvent>();
|
||||||
@ -20,13 +21,30 @@ export class ScrollView {
|
|||||||
private _lsn: Function;
|
private _lsn: Function;
|
||||||
private _endTmr: Function;
|
private _endTmr: Function;
|
||||||
|
|
||||||
ev: ScrollEvent = {
|
|
||||||
directionY: ScrollDirection.Down,
|
|
||||||
directionX: null
|
|
||||||
};
|
|
||||||
|
|
||||||
|
constructor(private _dom: DomController) {
|
||||||
constructor(private _dom: DomController) {}
|
this.ev = {
|
||||||
|
timeStamp: 0,
|
||||||
|
scrollTop: 0,
|
||||||
|
scrollLeft: 0,
|
||||||
|
startY: 0,
|
||||||
|
startX: 0,
|
||||||
|
deltaY: 0,
|
||||||
|
deltaX: 0,
|
||||||
|
velocityY: 0,
|
||||||
|
velocityX: 0,
|
||||||
|
directionY: 'down',
|
||||||
|
directionX: null,
|
||||||
|
domWrite: function(fn: DomCallback, ctx?: any) {
|
||||||
|
_dom.write(fn, ctx);
|
||||||
|
},
|
||||||
|
contentElement: null,
|
||||||
|
fixedElement: null,
|
||||||
|
scrollElement: null,
|
||||||
|
headerElement: null,
|
||||||
|
footerElement: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
init(ele: HTMLElement, contentTop: number, contentBottom: number) {
|
init(ele: HTMLElement, contentTop: number, contentBottom: number) {
|
||||||
if (!this.initialized) {
|
if (!this.initialized) {
|
||||||
@ -111,8 +129,8 @@ export class ScrollView {
|
|||||||
ev.velocityX = ((movedLeft / timeOffset) * FRAME_MS);
|
ev.velocityX = ((movedLeft / timeOffset) * FRAME_MS);
|
||||||
|
|
||||||
// figure out which direction we're scrolling
|
// figure out which direction we're scrolling
|
||||||
ev.directionY = (movedTop > 0 ? ScrollDirection.Up : ScrollDirection.Down);
|
ev.directionY = (movedTop > 0 ? 'up' : 'down');
|
||||||
ev.directionX = (movedLeft > 0 ? ScrollDirection.Left : ScrollDirection.Right);
|
ev.directionX = (movedLeft > 0 ? 'left' : 'right');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +139,7 @@ export class ScrollView {
|
|||||||
|
|
||||||
// debounce for a moment after the last scroll event
|
// debounce for a moment after the last scroll event
|
||||||
self._endTmr && self._endTmr();
|
self._endTmr && self._endTmr();
|
||||||
self._endTmr = rafFrames(5, function scrollEnd() {
|
self._endTmr = rafFrames(6, function scrollEnd() {
|
||||||
// haven't scrolled in a while, so it's a scrollend
|
// haven't scrolled in a while, so it's a scrollend
|
||||||
self.isScrolling = false;
|
self.isScrolling = false;
|
||||||
|
|
||||||
@ -345,7 +363,7 @@ export class ScrollView {
|
|||||||
if (this._js) {
|
if (this._js) {
|
||||||
return this._t;
|
return this._t;
|
||||||
}
|
}
|
||||||
return this._t = this._el.scrollTop || 0;
|
return this._t = this._el.scrollTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -355,7 +373,7 @@ export class ScrollView {
|
|||||||
if (this._js) {
|
if (this._js) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return this._l = this._el.scrollLeft || 0;
|
return this._l = this._el.scrollLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -492,34 +510,32 @@ export class ScrollView {
|
|||||||
this.stop();
|
this.stop();
|
||||||
this._endTmr && this._endTmr();
|
this._endTmr && this._endTmr();
|
||||||
this._lsn && this._lsn();
|
this._lsn && this._lsn();
|
||||||
this._el = this._dom = null;
|
let ev = this.ev;
|
||||||
|
ev.domWrite = ev.contentElement = ev.fixedElement = ev.scrollElement = ev.headerElement = null;
|
||||||
|
this._lsn = this._el = this._dom = this.ev = ev = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface ScrollEvent {
|
export interface ScrollEvent {
|
||||||
scrollTop?: number;
|
timeStamp: number;
|
||||||
scrollLeft?: number;
|
scrollTop: number;
|
||||||
startY?: number;
|
scrollLeft: number;
|
||||||
startX?: number;
|
startY: number;
|
||||||
deltaY?: number;
|
startX: number;
|
||||||
deltaX?: number;
|
deltaY: number;
|
||||||
timeStamp?: number;
|
deltaX: number;
|
||||||
velocityY?: number;
|
velocityY: number;
|
||||||
velocityX?: number;
|
velocityX: number;
|
||||||
directionY?: ScrollDirection;
|
directionY: string;
|
||||||
directionX?: ScrollDirection;
|
directionX: string;
|
||||||
}
|
domWrite: {(fn: DomCallback, ctx?: any)};
|
||||||
|
contentElement: HTMLElement;
|
||||||
|
fixedElement: HTMLElement;
|
||||||
export enum ScrollDirection {
|
scrollElement: HTMLElement;
|
||||||
Up, Down, Left, Right
|
headerElement: HTMLElement;
|
||||||
}
|
footerElement: HTMLElement;
|
||||||
|
|
||||||
|
|
||||||
export interface DomFn {
|
|
||||||
(callback: Function): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user