From c20bf15025cd511eef64702ac5b2ae89a117194e Mon Sep 17 00:00:00 2001 From: guilhermefloriani Date: Mon, 17 Jul 2017 14:57:50 -0300 Subject: [PATCH 1/9] Using requestAnimationFrame instead of setTimeout to ensure that the debounce scroll callback will be executed in the expected time. --- source/Grid/Grid.js | 18 +++++++++++------- source/Masonry/Masonry.js | 18 +++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index d10268b6c..982385522 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -979,16 +979,20 @@ export default class Grid extends PureComponent { * This prevents jerky/stuttery mouse-wheel scrolling. */ _debounceScrollEnded () { - const { scrollingResetTimeInterval } = this.props - if (this._disablePointerEventsTimeoutId) { - clearTimeout(this._disablePointerEventsTimeoutId) + window.cancelAnimationFrame(this._disablePointerEventsTimeoutId) } - this._disablePointerEventsTimeoutId = setTimeout( - this._debounceScrollEndedCallback, - scrollingResetTimeInterval - ) + const delay = () => { + if (Date.now() - this._scrollDebounceStart >= this.props.scrollingResetTimeInterval) { + this._debounceScrollEndedCallback() + } else { + this._disablePointerEventsTimeoutId = window.requestAnimationFrame(delay) + } + } + + this._scrollDebounceStart = Date.now() + this._disablePointerEventsTimeoutId = window.requestAnimationFrame(delay) } _debounceScrollEndedCallback () { diff --git a/source/Masonry/Masonry.js b/source/Masonry/Masonry.js index de9f4e9cc..09963badf 100644 --- a/source/Masonry/Masonry.js +++ b/source/Masonry/Masonry.js @@ -281,16 +281,20 @@ export default class Masonry extends PureComponent { } _debounceResetIsScrolling () { - const { scrollingResetTimeInterval } = this.props - if (this._debounceResetIsScrollingId) { - clearTimeout(this._debounceResetIsScrollingId) + window.cancelAnimationFrame(this._debounceResetIsScrollingId) } - this._debounceResetIsScrollingId = setTimeout( - this._debounceResetIsScrollingCallback, - scrollingResetTimeInterval - ) + const delay = () => { + if (Date.now() - this._scrollDebounceStart >= this.props.scrollingResetTimeInterval) { + this._debounceResetIsScrollingCallback() + } else { + this._debounceResetIsScrollingId = window.requestAnimationFrame(delay) + } + } + + this._scrollDebounceStart = Date.now() + this._debounceResetIsScrollingId = window.requestAnimationFrame(delay) } _debounceResetIsScrollingCallback () { From d534319503f826d956f72b9a84f528939cce0afb Mon Sep 17 00:00:00 2001 From: guilhermefloriani Date: Mon, 17 Jul 2017 15:38:11 -0300 Subject: [PATCH 2/9] Fixing tests after start to using requestAnimationFrame and cancelAnimationFrame. setTimout with zero delay was used in Masonry test to wait tasks of requestAnimationFrame finish before assert the result. --- source/Masonry/Masonry.jest.js | 4 +--- source/jest-setup.js | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/source/Masonry/Masonry.jest.js b/source/Masonry/Masonry.jest.js index 314b154ae..6b158470e 100644 --- a/source/Masonry/Masonry.jest.js +++ b/source/Masonry/Masonry.jest.js @@ -257,15 +257,13 @@ describe('Masonry', () => { }) it('should be reset after a small debounce when scrolling stops', () => { - jest.useFakeTimers() const cellMeasurerCache = createCellMeasurerCache() const renderCallback = jest.fn().mockImplementation(index => index) const cellRenderer = createCellRenderer(cellMeasurerCache, renderCallback) const rendered = findDOMNode(render(getMarkup({ cellMeasurerCache, cellRenderer }))) simulateScroll(rendered, 51) renderCallback.mockClear() - jest.runOnlyPendingTimers() - expect(renderCallback.mock.calls[0][1].isScrolling).toEqual(false) + setTimeout(() => expect(renderCallback.mock.calls[0][1].isScrolling).toEqual(false), 0) }) }) diff --git a/source/jest-setup.js b/source/jest-setup.js index d0d8597c0..bedc19777 100644 --- a/source/jest-setup.js +++ b/source/jest-setup.js @@ -5,4 +5,4 @@ jest.mock('dom-helpers/util/scrollbarSize', () => { }) // Polyfill requestAnimationFrame() for ReactDOMFrameScheduling -global.requestAnimationFrame = require('raf') +require('raf').polyfill() From aa4bcfdcd06ab8e69735acd1af268e81428b3fd1 Mon Sep 17 00:00:00 2001 From: guilhermefloriani Date: Mon, 17 Jul 2017 14:57:50 -0300 Subject: [PATCH 3/9] Using requestAnimationFrame instead of setTimeout to ensure that the debounce scroll callback will be executed in the expected time. --- source/Grid/Grid.js | 20 ++++++++++++-------- source/Masonry/Masonry.js | 20 ++++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index 58fc47015..432fe215e 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -1041,17 +1041,21 @@ export default class Grid extends PureComponent { * This flag is used to disable pointer events on the scrollable portion of the Grid. * This prevents jerky/stuttery mouse-wheel scrolling. */ - _debounceScrollEnded() { - const { scrollingResetTimeInterval } = this.props; - + _debounceScrollEnded () { if (this._disablePointerEventsTimeoutId) { - clearTimeout(this._disablePointerEventsTimeoutId); + window.cancelAnimationFrame(this._disablePointerEventsTimeoutId) } - this._disablePointerEventsTimeoutId = setTimeout( - this._debounceScrollEndedCallback, - scrollingResetTimeInterval - ); + const delay = () => { + if (Date.now() - this._scrollDebounceStart >= this.props.scrollingResetTimeInterval) { + this._debounceScrollEndedCallback() + } else { + this._disablePointerEventsTimeoutId = window.requestAnimationFrame(delay) + } + } + + this._scrollDebounceStart = Date.now() + this._disablePointerEventsTimeoutId = window.requestAnimationFrame(delay) } _debounceScrollEndedCallback() { diff --git a/source/Masonry/Masonry.js b/source/Masonry/Masonry.js index 6644d3495..7b4d2d3fb 100644 --- a/source/Masonry/Masonry.js +++ b/source/Masonry/Masonry.js @@ -290,17 +290,21 @@ export default class Masonry extends PureComponent { } } - _debounceResetIsScrolling() { - const { scrollingResetTimeInterval } = this.props; - + _debounceResetIsScrolling () { if (this._debounceResetIsScrollingId) { - clearTimeout(this._debounceResetIsScrollingId); + window.cancelAnimationFrame(this._debounceResetIsScrollingId) } - this._debounceResetIsScrollingId = setTimeout( - this._debounceResetIsScrollingCallback, - scrollingResetTimeInterval - ); + const delay = () => { + if (Date.now() - this._scrollDebounceStart >= this.props.scrollingResetTimeInterval) { + this._debounceResetIsScrollingCallback() + } else { + this._debounceResetIsScrollingId = window.requestAnimationFrame(delay) + } + } + + this._scrollDebounceStart = Date.now() + this._debounceResetIsScrollingId = window.requestAnimationFrame(delay) } _debounceResetIsScrollingCallback() { From a706712e27ca053b4c76839c355babf944a4f9b5 Mon Sep 17 00:00:00 2001 From: guilhermefloriani Date: Mon, 17 Jul 2017 15:38:11 -0300 Subject: [PATCH 4/9] Fixing tests after start to using requestAnimationFrame and cancelAnimationFrame. setTimout with zero delay was used in Masonry test to wait tasks of requestAnimationFrame finish before assert the result. --- source/Masonry/Masonry.jest.js | 27 ++++++++++----------------- source/jest-setup.js | 2 +- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/source/Masonry/Masonry.jest.js b/source/Masonry/Masonry.jest.js index 7b202b086..005ffe6d3 100644 --- a/source/Masonry/Masonry.jest.js +++ b/source/Masonry/Masonry.jest.js @@ -287,23 +287,16 @@ describe("Masonry", () => { expect(renderCallback.mock.calls[0][1].isScrolling).toEqual(true); }); - it("should be reset after a small debounce when scrolling stops", () => { - jest.useFakeTimers(); - const cellMeasurerCache = createCellMeasurerCache(); - const renderCallback = jest.fn().mockImplementation(index => index); - const cellRenderer = createCellRenderer( - cellMeasurerCache, - renderCallback - ); - const rendered = findDOMNode( - render(getMarkup({ cellMeasurerCache, cellRenderer })) - ); - simulateScroll(rendered, 51); - renderCallback.mockClear(); - jest.runOnlyPendingTimers(); - expect(renderCallback.mock.calls[0][1].isScrolling).toEqual(false); - }); - }); + it('should be reset after a small debounce when scrolling stops', () => { + const cellMeasurerCache = createCellMeasurerCache() + const renderCallback = jest.fn().mockImplementation(index => index) + const cellRenderer = createCellRenderer(cellMeasurerCache, renderCallback) + const rendered = findDOMNode(render(getMarkup({ cellMeasurerCache, cellRenderer }))) + simulateScroll(rendered, 51) + renderCallback.mockClear() + setTimeout(() => expect(renderCallback.mock.calls[0][1].isScrolling).toEqual(false), 0) + }) + }) describe("callbacks", () => { it("should call onCellsRendered when rendered cells change", () => { diff --git a/source/jest-setup.js b/source/jest-setup.js index 2b349bd1c..4b6907732 100644 --- a/source/jest-setup.js +++ b/source/jest-setup.js @@ -5,4 +5,4 @@ jest.mock("dom-helpers/util/scrollbarSize", () => { }); // Polyfill requestAnimationFrame() for ReactDOMFrameScheduling -global.requestAnimationFrame = require("raf"); +require('raf').polyfill() From 089af39b4e664f874e22d822bf08f2a94fd31537 Mon Sep 17 00:00:00 2001 From: guilhermefloriani Date: Mon, 17 Jul 2017 17:06:13 -0300 Subject: [PATCH 5/9] Avoid use a chain lookup --- source/Grid/Grid.js | 4 +++- source/Masonry/Masonry.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index 432fe215e..6eaf9c991 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -1042,12 +1042,14 @@ export default class Grid extends PureComponent { * This prevents jerky/stuttery mouse-wheel scrolling. */ _debounceScrollEnded () { + const { scrollingResetTimeInterval } = this.props; + if (this._disablePointerEventsTimeoutId) { window.cancelAnimationFrame(this._disablePointerEventsTimeoutId) } const delay = () => { - if (Date.now() - this._scrollDebounceStart >= this.props.scrollingResetTimeInterval) { + if (Date.now() - this._scrollDebounceStart >= scrollingResetTimeInterval) { this._debounceScrollEndedCallback() } else { this._disablePointerEventsTimeoutId = window.requestAnimationFrame(delay) diff --git a/source/Masonry/Masonry.js b/source/Masonry/Masonry.js index 7b4d2d3fb..992229efd 100644 --- a/source/Masonry/Masonry.js +++ b/source/Masonry/Masonry.js @@ -291,12 +291,14 @@ export default class Masonry extends PureComponent { } _debounceResetIsScrolling () { + const { scrollingResetTimeInterval } = this.props; + if (this._debounceResetIsScrollingId) { window.cancelAnimationFrame(this._debounceResetIsScrollingId) } const delay = () => { - if (Date.now() - this._scrollDebounceStart >= this.props.scrollingResetTimeInterval) { + if (Date.now() - this._scrollDebounceStart >= scrollingResetTimeInterval) { this._debounceResetIsScrollingCallback() } else { this._debounceResetIsScrollingId = window.requestAnimationFrame(delay) From 3264b83688b75a56b94c10de700448255178b23b Mon Sep 17 00:00:00 2001 From: guilhermefloriani Date: Thu, 20 Jul 2017 10:19:13 -0300 Subject: [PATCH 6/9] Avoid allocating one function for each scroll ended callback. --- source/Grid/Grid.js | 37 ++++++++++++++++++++-------------- source/Masonry/Masonry.jest.js | 27 ++++++++++++++++--------- source/Masonry/Masonry.js | 33 ++++++++++++++++++------------ source/jest-setup.js | 2 +- 4 files changed, 60 insertions(+), 39 deletions(-) diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index 6eaf9c991..13bdf68de 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -289,6 +289,7 @@ export default class Grid extends PureComponent { ); this._onScroll = this._onScroll.bind(this); this._setScrollingContainerRef = this._setScrollingContainerRef.bind(this); + this._delayScrollEnded = this._delayScrollEnded.bind(this); this._columnWidthGetter = this._wrapSizeGetter(props.columnWidth); this._rowHeightGetter = this._wrapSizeGetter(props.rowHeight); @@ -1036,28 +1037,34 @@ export default class Grid extends PureComponent { } } + /** + * Check if the difference between current time and the last scroll ended event is greater. + * than the scrollingResetTimeInterval prop, else schedule this function to execute again. + */ + _delayScrollEnded() { + const { scrollingResetTimeInterval } = this.props; + if (Date.now() - this._scrollDebounceStart >= scrollingResetTimeInterval) { + this._debounceScrollEndedCallback(); + } else { + this._disablePointerEventsTimeoutId = window.requestAnimationFrame( + this._delayScrollEnded + ); + } + } + /** * Sets an :isScrolling flag for a small window of time. * This flag is used to disable pointer events on the scrollable portion of the Grid. * This prevents jerky/stuttery mouse-wheel scrolling. */ - _debounceScrollEnded () { - const { scrollingResetTimeInterval } = this.props; - + _debounceScrollEnded() { if (this._disablePointerEventsTimeoutId) { - window.cancelAnimationFrame(this._disablePointerEventsTimeoutId) + window.cancelAnimationFrame(this._disablePointerEventsTimeoutId); } - - const delay = () => { - if (Date.now() - this._scrollDebounceStart >= scrollingResetTimeInterval) { - this._debounceScrollEndedCallback() - } else { - this._disablePointerEventsTimeoutId = window.requestAnimationFrame(delay) - } - } - - this._scrollDebounceStart = Date.now() - this._disablePointerEventsTimeoutId = window.requestAnimationFrame(delay) + this._scrollDebounceStart = Date.now(); + this._disablePointerEventsTimeoutId = window.requestAnimationFrame( + this._delayScrollEnded + ); } _debounceScrollEndedCallback() { diff --git a/source/Masonry/Masonry.jest.js b/source/Masonry/Masonry.jest.js index 005ffe6d3..3cd5b3a14 100644 --- a/source/Masonry/Masonry.jest.js +++ b/source/Masonry/Masonry.jest.js @@ -287,16 +287,23 @@ describe("Masonry", () => { expect(renderCallback.mock.calls[0][1].isScrolling).toEqual(true); }); - it('should be reset after a small debounce when scrolling stops', () => { - const cellMeasurerCache = createCellMeasurerCache() - const renderCallback = jest.fn().mockImplementation(index => index) - const cellRenderer = createCellRenderer(cellMeasurerCache, renderCallback) - const rendered = findDOMNode(render(getMarkup({ cellMeasurerCache, cellRenderer }))) - simulateScroll(rendered, 51) - renderCallback.mockClear() - setTimeout(() => expect(renderCallback.mock.calls[0][1].isScrolling).toEqual(false), 0) - }) - }) + it("should be reset after a small debounce when scrolling stops", () => { + const cellMeasurerCache = createCellMeasurerCache(); + const renderCallback = jest.fn().mockImplementation(index => index); + const cellRenderer = createCellRenderer( + cellMeasurerCache, + renderCallback + ); + const rendered = findDOMNode( + render(getMarkup({ cellMeasurerCache, cellRenderer })) + ); + simulateScroll(rendered, 51); + renderCallback.mockClear(); + setTimeout(() => { + expect(renderCallback.mock.calls[0][1].isScrolling).toEqual(false); + }, 0); + }); + }); describe("callbacks", () => { it("should call onCellsRendered when rendered cells change", () => { diff --git a/source/Masonry/Masonry.js b/source/Masonry/Masonry.js index 992229efd..97de5c60f 100644 --- a/source/Masonry/Masonry.js +++ b/source/Masonry/Masonry.js @@ -75,6 +75,7 @@ export default class Masonry extends PureComponent { ); this._setScrollingContainerRef = this._setScrollingContainerRef.bind(this); this._onScroll = this._onScroll.bind(this); + this._delayScrollEnded = this._delayScrollEnded.bind(this); } clearCellPositions() { @@ -290,23 +291,29 @@ export default class Masonry extends PureComponent { } } - _debounceResetIsScrolling () { + /** + * Check if the difference between current time and the last scroll ended event is greater. + * than the scrollingResetTimeInterval prop, else schedule this function to execute again. + */ + _delayScrollEnded() { const { scrollingResetTimeInterval } = this.props; - - if (this._debounceResetIsScrollingId) { - window.cancelAnimationFrame(this._debounceResetIsScrollingId) + if (Date.now() - this._scrollDebounceStart >= scrollingResetTimeInterval) { + this._debounceScrollEndedCallback(); + } else { + this._disablePointerEventsTimeoutId = window.requestAnimationFrame( + this._delayScrollEnded + ); } + } - const delay = () => { - if (Date.now() - this._scrollDebounceStart >= scrollingResetTimeInterval) { - this._debounceResetIsScrollingCallback() - } else { - this._debounceResetIsScrollingId = window.requestAnimationFrame(delay) - } + _debounceResetIsScrolling() { + if (this._debounceResetIsScrollingId) { + window.cancelAnimationFrame(this._debounceResetIsScrollingId); } - - this._scrollDebounceStart = Date.now() - this._debounceResetIsScrollingId = window.requestAnimationFrame(delay) + this._scrollDebounceStart = Date.now(); + this._debounceResetIsScrollingId = window.requestAnimationFrame( + this._delayScrollEnded + ); } _debounceResetIsScrollingCallback() { diff --git a/source/jest-setup.js b/source/jest-setup.js index 4b6907732..370b6c827 100644 --- a/source/jest-setup.js +++ b/source/jest-setup.js @@ -5,4 +5,4 @@ jest.mock("dom-helpers/util/scrollbarSize", () => { }); // Polyfill requestAnimationFrame() for ReactDOMFrameScheduling -require('raf').polyfill() +require("raf").polyfill(); From d782956d20c6151a2e2f277076a5f17d38340659 Mon Sep 17 00:00:00 2001 From: guilhermefloriani Date: Wed, 9 Aug 2017 08:52:14 -0300 Subject: [PATCH 7/9] Use cancelAnimationFrame instead of clearTimeOut on componentWillUnmount. --- source/Grid/Grid.js | 2 +- source/Masonry/Masonry.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index 27cc2a266..2f7b95d0e 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -783,7 +783,7 @@ export default class Grid extends PureComponent { componentWillUnmount() { if (this._disablePointerEventsTimeoutId) { - clearTimeout(this._disablePointerEventsTimeoutId); + window.cancelAnimationFrame(this._disablePointerEventsTimeoutId); } } diff --git a/source/Masonry/Masonry.js b/source/Masonry/Masonry.js index 97de5c60f..9ff5bcb13 100644 --- a/source/Masonry/Masonry.js +++ b/source/Masonry/Masonry.js @@ -123,7 +123,7 @@ export default class Masonry extends PureComponent { componentWillUnmount() { if (this._debounceResetIsScrollingId) { - clearTimeout(this._debounceResetIsScrollingId); + window.cancelAnimationFrame(this._debounceResetIsScrollingId); } } From 477ac94c6a6d3ad5fe3ff363ddde3a0904cd5cca Mon Sep 17 00:00:00 2001 From: Jared Lunde Date: Wed, 9 Aug 2017 16:37:55 -0500 Subject: [PATCH 8/9] Encapsulates scroll end debouncing into a timeout function relying on requestAnimationFrame This update also replaces setTimeout/clearTimeout in the WindowScroller component with the requestAnimationFrame-based timeout function. --- source/Grid/Grid.js | 35 +++++++++---------------- source/Masonry/Masonry.js | 30 +++++++-------------- source/WindowScroller/utils/onScroll.js | 12 ++++++--- source/utils/requestAnimationTimeout.js | 26 ++++++++++++++++++ 4 files changed, 58 insertions(+), 45 deletions(-) create mode 100644 source/utils/requestAnimationTimeout.js diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index 90dc03438..fdf53d6a5 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -26,6 +26,10 @@ import defaultOverscanIndicesGetter, { import updateScrollIndexHelper from "./utils/updateScrollIndexHelper"; import defaultCellRangeRenderer from "./defaultCellRangeRenderer"; import scrollbarSize from "dom-helpers/util/scrollbarSize"; +import requestAnimationTimeout, { + cancelAnimationTimeout +} from '../utils/requestAnimationTimeout' + /** * Specifies the number of milliseconds during which to disable pointer events while a scroll is in progress. @@ -298,7 +302,6 @@ export default class Grid extends React.PureComponent { _rowStopIndex: number; _disablePointerEventsTimeoutId: ?number; - _scrollDebounceStart: number; constructor(props: Props) { super(props); @@ -727,7 +730,7 @@ export default class Grid extends React.PureComponent { componentWillUnmount() { if (this._disablePointerEventsTimeoutId) { - window.cancelAnimationFrame(this._disablePointerEventsTimeoutId); + cancelAnimationTimeout(this._disablePointerEventsTimeoutId); } } @@ -1069,34 +1072,22 @@ export default class Grid extends React.PureComponent { } } - /** - * Check if the difference between current time and the last scroll ended event is greater. - * than the scrollingResetTimeInterval prop, else schedule this function to execute again. - */ - _delayScrollEnded = () => { - const { scrollingResetTimeInterval } = this.props; - if (Date.now() - this._scrollDebounceStart >= scrollingResetTimeInterval) { - this._debounceScrollEndedCallback(); - } else { - this._disablePointerEventsTimeoutId = window.requestAnimationFrame( - this._delayScrollEnded - ); - } - }; - /** * Sets an :isScrolling flag for a small window of time. * This flag is used to disable pointer events on the scrollable portion of the Grid. * This prevents jerky/stuttery mouse-wheel scrolling. */ _debounceScrollEnded() { + const { scrollingResetTimeInterval } = this.props; + if (this._disablePointerEventsTimeoutId) { - window.cancelAnimationFrame(this._disablePointerEventsTimeoutId); + cancelAnimationTimeout(this._disablePointerEventsTimeoutId); } - this._scrollDebounceStart = Date.now(); - this._disablePointerEventsTimeoutId = window.requestAnimationFrame( - this._delayScrollEnded - ); + + this._disablePointerEventsTimeoutId = requestAnimationTimeout( + this._debounceScrollEndedCallback, + scrollingResetTimeInterval + ) } _debounceScrollEndedCallback = () => { diff --git a/source/Masonry/Masonry.js b/source/Masonry/Masonry.js index 9ff5bcb13..d626498b1 100644 --- a/source/Masonry/Masonry.js +++ b/source/Masonry/Masonry.js @@ -2,6 +2,9 @@ import React, { PureComponent } from "react"; import cn from "classnames"; import PositionCache from "./PositionCache"; +import requestAnimationTimeout, { + cancelAnimationTimeout +} from '../utils/requestAnimationTimeout' const emptyObject = {}; @@ -75,7 +78,6 @@ export default class Masonry extends PureComponent { ); this._setScrollingContainerRef = this._setScrollingContainerRef.bind(this); this._onScroll = this._onScroll.bind(this); - this._delayScrollEnded = this._delayScrollEnded.bind(this); } clearCellPositions() { @@ -123,7 +125,7 @@ export default class Masonry extends PureComponent { componentWillUnmount() { if (this._debounceResetIsScrollingId) { - window.cancelAnimationFrame(this._debounceResetIsScrollingId); + cancelAnimationTimeout(this._debounceResetIsScrollingId); } } @@ -291,28 +293,16 @@ export default class Masonry extends PureComponent { } } - /** - * Check if the difference between current time and the last scroll ended event is greater. - * than the scrollingResetTimeInterval prop, else schedule this function to execute again. - */ - _delayScrollEnded() { + _debounceResetIsScrolling() { const { scrollingResetTimeInterval } = this.props; - if (Date.now() - this._scrollDebounceStart >= scrollingResetTimeInterval) { - this._debounceScrollEndedCallback(); - } else { - this._disablePointerEventsTimeoutId = window.requestAnimationFrame( - this._delayScrollEnded - ); - } - } - _debounceResetIsScrolling() { if (this._debounceResetIsScrollingId) { - window.cancelAnimationFrame(this._debounceResetIsScrollingId); + cancelAnimationTimeout(this._debounceResetIsScrollingId); } - this._scrollDebounceStart = Date.now(); - this._debounceResetIsScrollingId = window.requestAnimationFrame( - this._delayScrollEnded + + this._debounceResetIsScrollingId = requestAnimationTimeout( + this._debounceResetIsScrollingCallback, + scrollingResetTimeInterval ); } diff --git a/source/WindowScroller/utils/onScroll.js b/source/WindowScroller/utils/onScroll.js index 9094d1e46..2c7652a4b 100644 --- a/source/WindowScroller/utils/onScroll.js +++ b/source/WindowScroller/utils/onScroll.js @@ -1,3 +1,9 @@ +import requestAnimationTimeout, { + cancelAnimationTimeout +} from '../../utils/requestAnimationTimeout' + + + let mountedInstances = []; let originalBodyPointerEvents = null; let disablePointerEventsTimeoutId = null; @@ -19,7 +25,7 @@ function enablePointerEventsAfterDelayCallback() { function enablePointerEventsAfterDelay() { if (disablePointerEventsTimeoutId) { - clearTimeout(disablePointerEventsTimeoutId); + cancelAnimationTimeout(disablePointerEventsTimeoutId); } var maximumTimeout = 0; @@ -30,7 +36,7 @@ function enablePointerEventsAfterDelay() { ); }); - disablePointerEventsTimeoutId = setTimeout( + disablePointerEventsTimeoutId = requestAnimationTimeout( enablePointerEventsAfterDelayCallback, maximumTimeout ); @@ -64,7 +70,7 @@ export function unregisterScrollListener(component, element) { if (!mountedInstances.length) { element.removeEventListener("scroll", onScrollWindow); if (disablePointerEventsTimeoutId) { - clearTimeout(disablePointerEventsTimeoutId); + cancelAnimationTimeout(disablePointerEventsTimeoutId); enablePointerEventsIfDisabled(); } } diff --git a/source/utils/requestAnimationTimeout.js b/source/utils/requestAnimationTimeout.js new file mode 100644 index 000000000..92fab0d6d --- /dev/null +++ b/source/utils/requestAnimationTimeout.js @@ -0,0 +1,26 @@ +export function cancelAnimationTimeout(frame) { + window.cancelAnimationFrame(frame.id); +} + +/** + * Recursively calls requestAnimationFrame until a specified delay has been met + * or exceeded. When the delay time has been reached the function you're timing + * out will be called. + * + * Credit: Joe Lambert (https://gist.github.com/joelambert/1002116#file-requesttimeout-js) + */ +export default function requestAnimationTimeout(func, delay) { + const start = Date.now(); + const frame = new Object(); + + function timeout() { + if (Date.now() - start >= delay) { + func.call(); + } else { + frame.id = window.requestAnimationFrame(timeout); + } + } + + frame.id = window.requestAnimationFrame(timeout); + return frame; +} From cf2f8d09b440442b09691478169e4b4389f7f9b7 Mon Sep 17 00:00:00 2001 From: guilhermefloriani Date: Thu, 10 Aug 2017 09:23:45 -0300 Subject: [PATCH 9/9] Fixing pretty warnings and use just named imports for requestAnimationTimeout. --- source/Grid/Grid.js | 8 ++++---- source/Masonry/Masonry.js | 5 +++-- source/WindowScroller/utils/onScroll.js | 7 +++---- source/utils/requestAnimationTimeout.js | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index fdf53d6a5..e94aba65d 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -26,10 +26,10 @@ import defaultOverscanIndicesGetter, { import updateScrollIndexHelper from "./utils/updateScrollIndexHelper"; import defaultCellRangeRenderer from "./defaultCellRangeRenderer"; import scrollbarSize from "dom-helpers/util/scrollbarSize"; -import requestAnimationTimeout, { +import { + requestAnimationTimeout, cancelAnimationTimeout -} from '../utils/requestAnimationTimeout' - +} from "../utils/requestAnimationTimeout"; /** * Specifies the number of milliseconds during which to disable pointer events while a scroll is in progress. @@ -1087,7 +1087,7 @@ export default class Grid extends React.PureComponent { this._disablePointerEventsTimeoutId = requestAnimationTimeout( this._debounceScrollEndedCallback, scrollingResetTimeInterval - ) + ); } _debounceScrollEndedCallback = () => { diff --git a/source/Masonry/Masonry.js b/source/Masonry/Masonry.js index d626498b1..a5a026b83 100644 --- a/source/Masonry/Masonry.js +++ b/source/Masonry/Masonry.js @@ -2,9 +2,10 @@ import React, { PureComponent } from "react"; import cn from "classnames"; import PositionCache from "./PositionCache"; -import requestAnimationTimeout, { +import { + requestAnimationTimeout, cancelAnimationTimeout -} from '../utils/requestAnimationTimeout' +} from "../utils/requestAnimationTimeout"; const emptyObject = {}; diff --git a/source/WindowScroller/utils/onScroll.js b/source/WindowScroller/utils/onScroll.js index 2c7652a4b..c6998f140 100644 --- a/source/WindowScroller/utils/onScroll.js +++ b/source/WindowScroller/utils/onScroll.js @@ -1,8 +1,7 @@ -import requestAnimationTimeout, { +import { + requestAnimationTimeout, cancelAnimationTimeout -} from '../../utils/requestAnimationTimeout' - - +} from "../../utils/requestAnimationTimeout"; let mountedInstances = []; let originalBodyPointerEvents = null; diff --git a/source/utils/requestAnimationTimeout.js b/source/utils/requestAnimationTimeout.js index 92fab0d6d..613dd08a5 100644 --- a/source/utils/requestAnimationTimeout.js +++ b/source/utils/requestAnimationTimeout.js @@ -9,7 +9,7 @@ export function cancelAnimationTimeout(frame) { * * Credit: Joe Lambert (https://gist.github.com/joelambert/1002116#file-requesttimeout-js) */ -export default function requestAnimationTimeout(func, delay) { +export function requestAnimationTimeout(func, delay) { const start = Date.now(); const frame = new Object();