From ee829e42d2eecccab602f304796c4787a480452c Mon Sep 17 00:00:00 2001 From: rr- Date: Sun, 12 Jun 2016 21:01:18 +0200 Subject: [PATCH] client/paging: improve endless scroll --- client/js/views/endless_page_view.js | 200 ++++++++++++++------------- 1 file changed, 102 insertions(+), 98 deletions(-) diff --git a/client/js/views/endless_page_view.js b/client/js/views/endless_page_view.js index 14892ae5..e9175a19 100644 --- a/client/js/views/endless_page_view.js +++ b/client/js/views/endless_page_view.js @@ -18,81 +18,31 @@ class EndlessPageView { const target = document.getElementById('content-holder'); const source = this._holderTemplate(); const pageHeaderHolder = source.querySelector('.page-header-holder'); - const pagesHolder = source.querySelector('.pages-holder'); + this._pagesHolder = source.querySelector('.pages-holder'); views.listenToMessages(source); views.showView(target, source); this._active = true; this._working = 0; + this._init = true; ctx.headerContext.target = pageHeaderHolder; if (ctx.headerRenderer) { ctx.headerRenderer.render(ctx.headerContext); } - const threshold = window.innerHeight / 3; - + this.threshold = window.innerHeight / 3; this.minPageShown = null; this.maxPageShown = null; this.totalPages = null; this.currentPage = null; - this._updater = () => { - if (this._working) { - return; - } - - let topPageNode = null; - let element = document.elementFromPoint(window.innerWidth / 2, 1); - while (element.parentNode !== null) { - if (element.classList.contains('page')) { - topPageNode = element; - break; - } - element = element.parentNode; - } - if (!topPageNode) { - return; - } - let topPageNumber = parseInt(topPageNode.getAttribute('data-page')); - if (topPageNumber !== this.currentPage) { - router.replace( - _formatUrl(ctx.clientUrl, topPageNumber), - {}, - false); - this.currentPage = topPageNumber; - } - - if (this.totalPages === null) { - return; - } - let scrollHeight = - document.documentElement.scrollHeight - - document.documentElement.clientHeight; - - if (this.minPageShown > 1 && window.scrollY - threshold < 0) { - this._loadPage(pagesHolder, ctx, this.minPageShown - 1, false) - .then(() => this._updater()); - } else if (this.maxPageShown < this.totalPages && - window.scrollY + threshold > scrollHeight) { - this._loadPage(pagesHolder, ctx, this.maxPageShown + 1, true) - .then(() => this._updater()); - } - }; - - this._loadPage(pagesHolder, ctx, ctx.searchQuery.page, true) - .then(pageNode => { - if (ctx.searchQuery.page > 1) { - window.scroll(0, pageNode.getBoundingClientRect().top); - } - this._updater(); - }); - window.addEventListener('scroll', this._updater, true); + this._loadPage(ctx, ctx.searchQuery.page, true); window.addEventListener('unload', this._scrollToTop, true); + this._probePageLoad(ctx); } unrender() { this._active = false; - window.removeEventListener('scroll', this._updater, true); window.removeEventListener('unload', this._scrollToTop, true); } @@ -100,61 +50,115 @@ class EndlessPageView { window.scroll(0, 0); } - _loadPage(pagesHolder, ctx, pageNumber, append) { + _probePageLoad(ctx) { + if (this._active) { + window.setTimeout(() => { + window.requestAnimationFrame(() => { + this._probePageLoad(ctx); + }); + }, 250); + } + + if (this._working) { + return; + } + + let topPageNode = null; + let element = document.elementFromPoint( + window.innerWidth / 2, + window.innerHeight / 2); + while (element.parentNode !== null) { + if (element.classList.contains('page')) { + topPageNode = element; + break; + } + element = element.parentNode; + } + if (!topPageNode) { + return; + } + let topPageNumber = parseInt(topPageNode.getAttribute('data-page')); + if (topPageNumber !== this.currentPage) { + router.replace( + _formatUrl(ctx.clientUrl, topPageNumber), + {}, + false); + this.currentPage = topPageNumber; + } + + if (this.totalPages === null) { + return; + } + let scrollHeight = + document.documentElement.scrollHeight - + document.documentElement.clientHeight; + + if (this.minPageShown > 1 && window.scrollY < this.threshold) { + this._loadPage(ctx, this.minPageShown - 1, false); + } else if (this.maxPageShown < this.totalPages && + window.scrollY + this.threshold > scrollHeight) { + this._loadPage(ctx, this.maxPageShown + 1, true); + } + } + + _loadPage(ctx, pageNumber, append) { this._working++; - return ctx.requestPage(pageNumber).then(response => { + ctx.requestPage(pageNumber).then(response => { if (!this._active) { this._working--; return Promise.reject(); } this.totalPages = Math.ceil(response.total / response.pageSize); - if (response.total) { - const pageNode = this._pageTemplate({ - page: pageNumber, - totalPages: this.totalPages, - }); - pageNode.setAttribute('data-page', pageNumber); - - Object.assign(ctx.pageContext, response); - ctx.pageContext.target = pageNode.querySelector( - '.page-content-holder'); - ctx.pageRenderer.render(ctx.pageContext); - - if (pageNumber < this.minPageShown || - this.minPageShown === null) { - this.minPageShown = pageNumber; - } - if (pageNumber > this.maxPageShown || - this.maxPageShown === null) { - this.maxPageShown = pageNumber; - } - - if (append) { - pagesHolder.appendChild(pageNode); - } else { - pagesHolder.prependChild(pageNode); - - // note: with probability of 75%, if the user has scrolled - // with a mouse wheel, chrome 49 doesn't give a slightest - // fuck about this and loads all the way to page 1 at once - window.scroll( - window.scrollX, - window.scrollY + pageNode.offsetHeight); - } + window.requestAnimationFrame(() => { + this._renderPage( + ctx, pageNumber, append, response); this._working--; - return Promise.resolve(pageNode); - } - if (response.total <= (pageNumber - 1) * response.pageSize) { - events.notify(events.Info, 'No data to show'); - } - this._working--; - return Promise.reject(); + }); }, response => { events.notify(events.Error, response.description); this._working--; - return Promise.reject(); }); } + + _renderPage(ctx, pageNumber, append, response) { + if (response.total) { + const pageNode = this._pageTemplate({ + page: pageNumber, + totalPages: this.totalPages, + }); + pageNode.setAttribute('data-page', pageNumber); + + Object.assign(ctx.pageContext, response); + ctx.pageContext.target = pageNode.querySelector( + '.page-content-holder'); + ctx.pageRenderer.render(ctx.pageContext); + + if (pageNumber < this.minPageShown || + this.minPageShown === null) { + this.minPageShown = pageNumber; + } + if (pageNumber > this.maxPageShown || + this.maxPageShown === null) { + this.maxPageShown = pageNumber; + } + + if (append) { + this._pagesHolder.appendChild(pageNode); + /*if (this._init && pageNumber !== 1) { + window.scroll(0, pageNode.getBoundingClientRect().top); + }*/ + } else { + this._pagesHolder.prependChild(pageNode); + + window.scroll( + window.scrollX, + window.scrollY + pageNode.offsetHeight); + } + } else if (response.total <= (pageNumber - 1) * response.pageSize) { + events.notify(events.Info, 'No data to show'); + } + this._init = false; + } } module.exports = EndlessPageView;