diff --git a/README.md b/README.md index 42ecca0..7911e1a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Smooth animated scrolling. Move elements into view, or scroll to any vertical position. -One kilobyte of pure JavaScript. No dependencies. +1.1 kilobyte of vanilla JavaScript. No dependencies. ## About @@ -27,8 +27,9 @@ Features: - Scrolling an element into view, making sure both top & bottom are visible, if possible. - Scroll to an element and center it on the screen. - Customize the duration of the individual scroll operations. +- If you provide a callback function it will be executed when the scrolling is done. - Specify the spacing between the element and the edge of the screen (e.g., for fixed navigation bars and footers). -- Just 1 kilobyte minimized & gzipped. +- Just 1.1 kilobyte minimized & gzipped. - No dependencies. Full support tested and works under: @@ -218,7 +219,34 @@ myScroller.intoView(target) ```` -### 8. Change settings +### 8. Execute something when the scrolling is done + +You can provide a callback function to all four scroll functions, which is executed when the scroll operation is finished. For example, you change some UI elements but first you want to make sure that the relevant elements are visible. + +If you look at the code examples above under the previous point, [7. Scroll inside a scrollable DIV](#7.scrollinsideascrollablediv) they are actually implemented like this: + +````js +// Last line of example 1: +zenscroll.intoView(container, 100, function () { myScroller.center(target) }) + +// Example 2: +zenscroll.intoView(container, 100, function () { myScroller.toY(35) }) + +// Example 3: +zenscroll.intoView(container, 100, function () { myScroller.intoView(target) }) +```` + +So first the container (with _ITEM 1_ to _ITEM 7_) is scrolled into view if necessary, and then the scrolling inside the container is performed. Try scrolling out the above container and then hit one of the ‘Play’ buttons above to see how it works. + +This works with all four scrolling functions. The `onDone` parameter is always the last parameter: + +1. `to(element, duration, onDone)` +1. `toY(y, duration, onDone)` +1. `intoView(element, duration, onDone)` +1. `center(element, duration, offset, onDone)` + + +### 9. Change settings It’s easy to change the basic parameters of scrolling: @@ -250,7 +278,7 @@ zenscroll.setup(null, 42) ```` -### 9. Controlling the smooth operation +### 10. Controlling the smooth operation To check whether a scoll is being performed right now: diff --git a/package.json b/package.json index 5a70f16..84f103b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zenscroll", - "version": "3.0.2", + "version": "3.1.0", "description": "A module to smooth-scroll web pages and DIVs", "main": "zenscroll.js", "files": ["zenscroll.js", "zenscroll-min.js"], diff --git a/zenscroll-min.js b/zenscroll-min.js index 5d52027..d6aab3f 100644 --- a/zenscroll-min.js +++ b/zenscroll-min.js @@ -1 +1 @@ -!function(t,e){"function"==typeof define&&define.amd?define([],e()):"object"==typeof module&&module.exports?module.exports=e():t.zenscroll=e()}(this,function(){"use strict";if(!window||!document)return{};var t=function(t,e,n){e=e||999,n||0===n||(n=9);var o,i=document.documentElement,r=function(){return"getComputedStyle"in window&&"smooth"===window.getComputedStyle(t?t:document.body)["scroll-behavior"]},c=function(){return t?t.scrollTop:window.scrollY||i.scrollTop},u=function(){return t?Math.min(t.offsetHeight,window.innerHeight):window.innerHeight||i.clientHeight},f=function(e){return t?e.offsetTop-t.offsetTop:e.getBoundingClientRect().top+c()-i.offsetTop},l=function(){clearTimeout(o),o=0},a=function(n,f){if(l(),r())(t||window).scrollTo(0,n);else{var a=c(),s=Math.max(n,0)-a;f=f||Math.min(Math.abs(s),e);var d=(new Date).getTime();!function m(){o=setTimeout(function(){var e=Math.min(((new Date).getTime()-d)/f,1),n=Math.max(Math.floor(a+s*(.5>e?2*e*e:e*(4-2*e)-1)),0);t?t.scrollTop=n:window.scrollTo(0,n),1>e&&u()+n<(t||i).scrollHeight?m():setTimeout(l,99)},9)}()}},s=function(t,e){a(f(t)-n,e)},d=function(t,e){var o=t.getBoundingClientRect().height+2*n,i=u(),r=f(t),l=r+o-n,d=c();n>r-d||o>i?s(t,e):0>d+i-l&&a(l-i,e)},m=function(t,e,n){a(Math.max(f(t)-u()/2+(n||t.getBoundingClientRect().height/2),0),e)},h=function(t,o){t&&(e=t),(0===o||o)&&(n=o)};return{setup:h,to:s,toY:a,intoView:d,center:m,stop:l,moving:function(){return!!o}}},e=t();if("addEventListener"in window&&"smooth"!==document.body.style.scrollBehavior&&!window.noZensmooth){var n=function(t){try{history.replaceState({},"",window.location.href.split("#")[0]+t)}catch(e){}};window.addEventListener("click",function(t){for(var o=t.target;o&&"A"!==o.tagName;)o=o.parentNode;if(!(!o||1!==t.which||t.shiftKey||t.metaKey||t.ctrlKey||t.altKey)){var i=o.getAttribute("href")||"";if(0===i.indexOf("#"))if("#"===i)t.preventDefault(),e.toY(0),n("");else{var r=o.hash.substring(1),c=document.getElementById(r);c&&(t.preventDefault(),e.to(c),n("#"+r))}}},!1)}return{createScroller:t,setup:e.setup,to:e.to,toY:e.toY,intoView:e.intoView,center:e.center,stop:e.stop,moving:e.moving}}); \ No newline at end of file +!function(t,e){"function"==typeof define&&define.amd?define([],e()):"object"==typeof module&&module.exports?module.exports=e():t.zenscroll=e()}(this,function(){"use strict";if(!window||!document)return{};var t=function(t,e,n){e=e||999,n||0===n||(n=9);var o,i=document.documentElement,r=function(){return"getComputedStyle"in window&&"smooth"===window.getComputedStyle(t?t:document.body)["scroll-behavior"]},c=function(){return t?t.scrollTop:window.scrollY||i.scrollTop},u=function(){return t?Math.min(t.offsetHeight,window.innerHeight):window.innerHeight||i.clientHeight},f=function(e){return t?e.offsetTop-t.offsetTop:e.getBoundingClientRect().top+c()-i.offsetTop},l=function(){clearTimeout(o),o=0},a=function(n,f,a){if(l(),r())(t||window).scrollTo(0,n),a&&a();else{var s=c(),d=Math.max(n,0)-s;f=f||Math.min(Math.abs(d),e);var m=(new Date).getTime();!function h(){o=setTimeout(function(){var e=Math.min(((new Date).getTime()-m)/f,1),n=Math.max(Math.floor(s+d*(.5>e?2*e*e:e*(4-2*e)-1)),0);t?t.scrollTop=n:window.scrollTo(0,n),1>e&&u()+n<(t||i).scrollHeight?h():(setTimeout(l,99),a&&a())},9)}()}},s=function(t,e,o){a(f(t)-n,e,o)},d=function(t,e,o){var i=t.getBoundingClientRect().height+2*n,r=u(),l=f(t),d=l+i-n,m=c();n>l-m||i>r?s(t,e,o):0>m+r-d?a(d-r,e,o):o&&o()},m=function(t,e,n,o){a(Math.max(f(t)-u()/2+(n||t.getBoundingClientRect().height/2),0),e,o)},h=function(t,o){t&&(e=t),(0===o||o)&&(n=o)};return{setup:h,to:s,toY:a,intoView:d,center:m,stop:l,moving:function(){return!!o}}},e=t();if("addEventListener"in window&&"smooth"!==document.body.style.scrollBehavior&&!window.noZensmooth){var n=function(t){try{history.replaceState({},"",window.location.href.split("#")[0]+t)}catch(e){}};window.addEventListener("click",function(t){for(var o=t.target;o&&"A"!==o.tagName;)o=o.parentNode;if(!(!o||1!==t.which||t.shiftKey||t.metaKey||t.ctrlKey||t.altKey)){var i=o.getAttribute("href")||"";if(0===i.indexOf("#"))if("#"===i)t.preventDefault(),e.toY(0),n("");else{var r=o.hash.substring(1),c=document.getElementById(r);c&&(t.preventDefault(),e.to(c),n("#"+r))}}},!1)}return{createScroller:t,setup:e.setup,to:e.to,toY:e.toY,intoView:e.intoView,center:e.center,stop:e.stop,moving:e.moving}}); \ No newline at end of file diff --git a/zenscroll.js b/zenscroll.js index f98f002..c3e7686 100644 --- a/zenscroll.js +++ b/zenscroll.js @@ -1,5 +1,5 @@ /** - * Zenscroll 3.0.2 + * Zenscroll 3.1.0 * https://github.com/zengabor/zenscroll/ * * Copyright 2015–2016 Gabor Lenard @@ -103,10 +103,13 @@ * If 0 or not provided it is automatically calculated based on the * distance and the default duration. */ - var scrollToY = function (endY, duration) { + var scrollToY = function (endY, duration, onDone) { stopScroll() if (nativeSmoothScrollEnabled()) { (scrollContainer || window).scrollTo(0, endY) + if (onDone) { + onDone() + } } else { var startY = getScrollTop() var distance = Math.max(endY,0) - startY @@ -125,6 +128,9 @@ loopScroll() } else { setTimeout(stopScroll, 99) // with cooldown time + if (onDone) { + onDone() + } } }, 9) })() @@ -138,8 +144,8 @@ * @param {duration} Optionally the duration of the scroll operation. * A value of 0 is ignored. */ - var scrollToElem = function (elem, duration) { - scrollToY(getRelativeTopOf(elem) - edgeOffset, duration) + var scrollToElem = function (elem, duration, onDone) { + scrollToY(getRelativeTopOf(elem) - edgeOffset, duration, onDone) } /** @@ -149,7 +155,7 @@ * @param {duration} Optionally the duration of the scroll operation. * A value of 0 is ignored. */ - var scrollIntoView = function (elem, duration) { + var scrollIntoView = function (elem, duration, onDone) { var elemScrollHeight = elem.getBoundingClientRect().height + 2*edgeOffset var vHeight = getViewHeight() var elemTop = getRelativeTopOf(elem) @@ -157,10 +163,12 @@ var scrollTop = getScrollTop() if ((elemTop - scrollTop) < edgeOffset || elemScrollHeight > vHeight) { // Element is clipped at top or is higher than screen. - scrollToElem(elem, duration) + scrollToElem(elem, duration, onDone) } else if ((scrollTop + vHeight - elemBottom) < 0) { // Element is clipped at the bottom. - scrollToY(elemBottom - vHeight, duration) + scrollToY(elemBottom - vHeight, duration, onDone) + } else if (onDone) { + onDone() } } @@ -172,13 +180,14 @@ * @param {offset} Optionally the offset of the top of the element from the center of the screen. * A value of 0 is ignored. */ - var scrollToCenterOf = function (elem, duration, offset) { + var scrollToCenterOf = function (elem, duration, offset, onDone) { scrollToY( Math.max( getRelativeTopOf(elem) - getViewHeight()/2 + (offset || elem.getBoundingClientRect().height/2), 0 ), - duration + duration, + onDone ) }