From e3c70c59531b78fdb2a1cd4fa4f257447c1aa2a9 Mon Sep 17 00:00:00 2001 From: RealGoodProgrammer Date: Wed, 28 Oct 2020 17:51:18 +0300 Subject: [PATCH] feat: add lazy option to allow recalculating targets (#388) * feat: add dynamicPosition parameter (for dynamic container height) * fix: rename option and small refactoring * chore: apply suggestions from code review * perf: remove redundant calculation Co-authored-by: Igor Randjelovic --- README.md | 8 ++++++++ src/scrollTo.js | 47 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0f4e14d4..e19c84cb 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,7 @@ In case you are using the browser version (directly including the script on your VueScrollTo.setDefaults({ container: "body", duration: 500, + lazy: false, easing: "ease", offset: 0, force: true, @@ -125,6 +126,7 @@ If you need to customize the scrolling options, you can pass in an object litera el: '#element', container: '#container', duration: 500, + lazy: false easing: 'linear', offset: -200, force: true, @@ -151,6 +153,7 @@ var VueScrollTo = require('vue-scrollto'); var options = { container: '#container', easing: 'ease-in', + lazy: false, offset: -60, force: true, cancelable: true, @@ -196,6 +199,11 @@ The easing to be used when animating. Read more in the [Easing section](#easing- *Default:* `ease` +#### lazy +By default targetX/targetY are calculated once at the start of a scroll, however if the target may shift around during the scroll - setting `lazy` to `false` will force recalculation of targetX/targetY at each scroll step. + +*Default:* `true` + #### offset The offset that should be applied when scrolling. This option accepts a callback function since `v2.8.0`. diff --git a/src/scrollTo.js b/src/scrollTo.js index 050eef7d..fe4950c1 100644 --- a/src/scrollTo.js +++ b/src/scrollTo.js @@ -14,6 +14,7 @@ const abortEvents = [ let defaults = { container: 'body', duration: 500, + lazy: true, easing: 'ease', offset: 0, force: true, @@ -34,6 +35,7 @@ export const scroller = () => { let container // container to scroll let duration // duration of the scrolling let easing // easing to be used when scrolling + let lazy // checks the target position at each step let offset // offset to be added (subtracted) let force // force scroll, even if element is visible let cancelable // indicates if user can cancel the scroll or not. @@ -52,6 +54,9 @@ export const scroller = () => { let abort // is scrolling aborted + let cumulativeOffsetContainer + let cumulativeOffsetElement + let abortEv // event that aborted scrolling let abortFn = e => { if (!cancelable) return @@ -91,10 +96,33 @@ export const scroller = () => { return scrollLeft } + function recalculateTargets() { + cumulativeOffsetContainer = _.cumulativeOffset(container) + cumulativeOffsetElement = _.cumulativeOffset(element) + + if (x) { + targetX = + cumulativeOffsetElement.left - cumulativeOffsetContainer.left + offset + diffX = targetX - initialX + } + if (y) { + targetY = + cumulativeOffsetElement.top - cumulativeOffsetContainer.top + offset + diffY = targetY - initialY + } + } + function step(timestamp) { if (abort) return done() if (!timeStart) timeStart = timestamp + // When a site has a lot of media that can be loaded asynchronously, + // the targetY/targetX may end up in the wrong place during scrolling. + // So we will check this at each step + if (!lazy) { + recalculateTargets() + } + timeElapsed = timestamp - timeStart progress = Math.min(timeElapsed / duration, 1) @@ -143,7 +171,10 @@ export const scroller = () => { } container = _.$(options.container || defaults.container) - duration = options.hasOwnProperty('duration') ? options.duration : defaults.duration + duration = options.hasOwnProperty('duration') + ? options.duration + : defaults.duration + lazy = options.hasOwnProperty('lazy') ? options.lazy : defaults.lazy easing = options.easing || defaults.easing offset = options.hasOwnProperty('offset') ? options.offset : defaults.offset force = options.hasOwnProperty('force') @@ -158,26 +189,18 @@ export const scroller = () => { x = options.x === undefined ? defaults.x : options.x y = options.y === undefined ? defaults.y : options.y - let cumulativeOffsetContainer = _.cumulativeOffset(container) - let cumulativeOffsetElement = _.cumulativeOffset(element) - if (typeof offset === 'function') { offset = offset(element, container) } + initialX = scrollLeft(container) initialY = scrollTop(container) - targetY = - cumulativeOffsetElement.top - cumulativeOffsetContainer.top + offset - initialX = scrollLeft(container) - targetX = - cumulativeOffsetElement.left - cumulativeOffsetContainer.left + offset + // calculates cumulative offsets and targetX/Y + diffX/Y + recalculateTargets() abort = false - diffY = targetY - initialY - diffX = targetX - initialX - if (!force) { // When the container is the default (body) we need to use the viewport // height, not the entire body height