diff --git a/core/src/components/scroll/scroll-interface.ts b/core/src/components/scroll/scroll-interface.ts index 73d88f3d7a..194614272a 100644 --- a/core/src/components/scroll/scroll-interface.ts +++ b/core/src/components/scroll/scroll-interface.ts @@ -5,7 +5,6 @@ export interface ScrollBaseDetail { } export interface ScrollDetail extends GestureDetail, ScrollBaseDetail { - positions: number[]; scrollTop: number; scrollLeft: number; } diff --git a/core/src/components/scroll/scroll.tsx b/core/src/components/scroll/scroll.tsx index 75b1a402f0..ccfcfedffb 100644 --- a/core/src/components/scroll/scroll.tsx +++ b/core/src/components/scroll/scroll.tsx @@ -13,9 +13,30 @@ export class Scroll { private watchDog: any; private isScrolling = false; private lastScroll = 0; - private detail: ScrollDetail; private queued = false; + // Detail is used in a hot loop in the scroll event, by allocating it here + // V8 will be able to inline any read/write to it since it's a monomorphic class. + // https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html + private detail: ScrollDetail = { + scrollTop: 0, + scrollLeft: 0, + type: 'scroll', + event: undefined!, + startX: 0, + startY: 0, + startTimeStamp: 0, + currentX: 0, + currentY: 0, + velocityX: 0, + velocityY: 0, + deltaX: 0, + deltaY: 0, + timeStamp: 0, + data: undefined, + isScrolling: true, + }; + @Element() el!: HTMLElement; @Prop({ context: 'config' }) config!: Config; @@ -51,31 +72,6 @@ export class Scroll { */ @Event() ionScrollEnd!: EventEmitter; - constructor() { - // Detail is used in a hot loop in the scroll event, by allocating it here - // V8 will be able to inline any read/write to it since it's a monomorphic class. - // https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html - this.detail = { - positions: [], - scrollTop: 0, - scrollLeft: 0, - type: 'scroll', - event: undefined!, - startX: 0, - startY: 0, - startTimeStamp: 0, - currentX: 0, - currentY: 0, - velocityX: 0, - velocityY: 0, - deltaX: 0, - deltaY: 0, - timeStamp: 0, - data: undefined, - isScrolling: true, - }; - } - componentWillLoad() { if (this.forceOverscroll === undefined) { this.forceOverscroll = this.mode === 'ios' && ('ontouchstart' in this.win); @@ -116,10 +112,7 @@ export class Scroll { /** Scroll to the bottom of the component */ @Method() scrollToBottom(duration: number): Promise { - const y = (this.el) - ? this.el.scrollHeight - this.el.clientHeight - : 0; - + const y = this.el.scrollHeight - this.el.clientHeight; return this.scrollToPoint(0, y, duration); } @@ -209,9 +202,8 @@ export class Scroll { // chill out for a frame first this.queue.write(() => { this.queue.write(timeStamp => { - // TODO: review stencilt type of timeStamp - startTime = timeStamp!; - step(timeStamp!); + startTime = timeStamp; + step(timeStamp); }); }); @@ -263,49 +255,32 @@ export class Scroll { function updateScrollDetail( detail: ScrollDetail, el: HTMLElement, - timeStamp: number, + timestamp: number, didStart: boolean ) { - const scrollTop = el.scrollTop; - const scrollLeft = el.scrollLeft; - const positions = detail.positions; + const prevX = detail.currentX; + const prevY = detail.currentY; + const prevT = detail.timeStamp; + const currentX = el.scrollLeft; + const currentY = el.scrollTop; if (didStart) { // remember the start positions - detail.startTimeStamp = timeStamp; - detail.startY = scrollTop; - detail.startX = scrollLeft; - positions.length = 0; + detail.startTimeStamp = timestamp; + detail.startX = currentX; + detail.startY = currentY; + detail.velocityX = detail.velocityY = 0; } + detail.timeStamp = timestamp; + detail.currentX = detail.scrollLeft = currentX; + detail.currentY = detail.scrollTop = currentY; + detail.deltaX = currentX - detail.startX; + detail.deltaY = currentY - detail.startY; - detail.timeStamp = timeStamp; - detail.currentY = detail.scrollTop = scrollTop; - detail.currentX = detail.scrollLeft = scrollLeft; - detail.deltaY = scrollTop - detail.startY; - detail.deltaX = scrollLeft - detail.startX; - - // actively scrolling - positions.push(scrollTop, scrollLeft, timeStamp); - - // move pointer to position measured 100ms ago - const timeRange = timeStamp - 100; - let startPos = positions.length - 1; - - while (startPos > 0 && positions[startPos] > timeRange) { - startPos -= 3; - } - - if (startPos > 3) { - // compute relative movement between these two points - const frequency = 1 / (positions[startPos] - timeStamp); - const movedX = positions[startPos - 1] - scrollLeft; - const movedY = positions[startPos - 2] - scrollTop; - - // based on XXms compute the movement to apply for each render step - // velocity = space/time = s*(1/t) = s*frequency - detail.velocityX = movedX * frequency; - detail.velocityY = movedY * frequency; - } else { - detail.velocityX = 0; - detail.velocityY = 0; + const timeDelta = timestamp - prevT; + if (timeDelta > 0 && timeDelta < 100) { + const velocityX = (currentX - prevX) / timeDelta; + const velocityY = (currentY - prevY) / timeDelta; + detail.velocityX = velocityX * 0.7 + detail.velocityX * 0.3; + detail.velocityY = velocityY * 0.7 + detail.velocityY * 0.3; } }