Commit db57125d authored by Daniel Kurowski's avatar Daniel Kurowski
Browse files

Merge branch 'upgrade' into 'master'

changed API of smoothscrolling

See merge request !4
parents 042931dc 71d08b01
import Velocity from 'velocity-animate';
// import smoothScroll from 'smoothscroll-polyfill'; In case of use, add `smoothscroll-polyfill` via `yarn add` first.
/**
* As of version 62, Chrome started to support natively `behavior: smooth` in `scrollIntoView()` method.
* But it works strange - it moves the viewport just to the half of the target container.
* Thus we have to force the polyfill to behave as intended.
* Related: https://github.com/iamdustan/smoothscroll/issues/93
*/
// window.__forceSmoothScrollPolyfill__ = true;
// smoothScroll.polyfill();
export default class {
constructor (windowObject, documentObject) {
this.windowObject = windowObject;
this.documentObject = documentObject;
export default function (options) {
((windowObject, documentObject, options) => {
//Registration of custom easing
Velocity.Easings['ease-in-skip-out'] = easingWithSkip(this.windowObject);
}
/**
* This function scrolls smoothly to an element if there is a hash in the URL.
* E.g. you have `#example` in URL, then it scrolls to element with id `example`.
*
* Note:
* Because of UX, this behaviour is limited only when whole document is loaded in less than 500ms.
* Otherwise, it jumps directly to desired element without smooth scrolling, because too visible jumping through the page would appear.
*/
const handleLoad = () => {
if (typeof document.querySelector === 'undefined') {
return;
}
if (typeof document.addEventListener === 'undefined') {
return;
}
if (typeof windowObject.location === 'undefined') {
return;
}
handleInteraction () {
if ((typeof document.querySelector === 'undefined') || (typeof document.querySelectorAll === 'undefined')) {
return;
}
if (typeof document.addEventListener === 'undefined') {
return;
}
// If no hash, we do not need to run scrolling.
if (!windowObject.location.hash) {
return;
}
// If performance is not present, the browser would not scroll smoothly otherwise I guess. So let's skip it completely, it's not worth fallbacking to Date() function.
if (typeof performance === 'undefined') {
return;
}
// Start timer.
const start = performance.now();
this.documentObject.addEventListener('DOMContentLoaded', () => {
const items = this.documentObject.querySelectorAll('a[href^="#"]');
/*
* The `load` event has been chosen intentionally as it is the state when everything is ready -
* all styles are loaded and offsets are computed correctly - so the scroll will be computed correctly.
*/
windowObject.addEventListener('load', () => {
// End timer.
const end = performance.now();
for (let i in items) {
if (typeof items[i] !== 'object') {
continue;
// If difference between start and stop is greater than 500ms, do nothing.
if (end - start > 500) {
return;
}
items[i].addEventListener('click', (e) => {
if (typeof e.currentTarget.getAttribute === 'undefined') {
return;
}
// First, we need to go to top immediately (hack to prevent jump to desired element).
windowObject.scroll({top: 0, left: 0});
const hash = e.currentTarget.getAttribute('href');
if (!hash) {
return;
// Then, scroll down to it smoothly.
scrollTo(windowObject.location.hash, documentObject, windowObject);
});
};
const handleInteraction = () => {
if ((typeof document.querySelector === 'undefined') || (typeof document.querySelectorAll === 'undefined')) {
return;
}
if (typeof document.addEventListener === 'undefined') {
return;
}
documentObject.addEventListener('DOMContentLoaded', () => {
const items = documentObject.querySelectorAll('a[href^="#"]');
for (let i in items) {
if (typeof items[i] !== 'object') {
continue;
}
e.preventDefault();
e.stopPropagation();
items[i].addEventListener('click', (e) => {
if (typeof e.currentTarget.getAttribute === 'undefined') {
return;
}
scrollTo(hash, this.documentObject, this.windowObject);
});
}
});
};
/**
* This function scrolls smoothly to an element if there is a hash in the URL.
* E.g. you have `#example` in URL, then it scrolls to element with id `example`.
*
* Note:
* Because of UX, this behaviour is limited only when whole document is loaded in less than 500ms.
* Otherwise, it jumps directly to desired element without smooth scrolling, because too visible jumping through the page would appear.
*/
handleLoad () {
if (typeof document.querySelector === 'undefined') {
return;
}
if (typeof document.addEventListener === 'undefined') {
return;
}
if (typeof this.windowObject.location === 'undefined') {
return;
}
const hash = e.currentTarget.getAttribute('href');
if (!hash) {
return;
}
// If no hash, we do not need to run scrolling.
if (!this.windowObject.location.hash) {
return;
}
e.preventDefault();
e.stopPropagation();
// If performance is not present, the browser would not scroll smoothly otherwise I guess. So let's skip it completely, it's not worth fallbacking to Date() function.
if (typeof performance === 'undefined') {
return;
}
scrollTo(hash, documentObject, windowObject);
});
}
});
};
// Start timer.
const start = performance.now();
/*
* The `load` event has been chosen intentionally as it is the state when everything is ready -
* all styles are loaded and offsets are computed correctly - so the scroll will be computed correctly.
*/
this.windowObject.addEventListener('load', () => {
// End timer.
const end = performance.now();
// Registration of custom easing
Velocity.Easings['ease-in-skip-out'] = easingWithSkip(windowObject);
// If difference between start and stop is greater than 500ms, do nothing.
if (end - start > 500) {
return;
}
// If `options.interaction` is not explicitly set to `false`, run handler.
if (!(options !== undefined && typeof options === 'object' && options.interaction !== undefined && options.interaction === false)) {
handleInteraction();
}
// First, we need to go to top immediately (hack to prevent jump to desired element).
this.windowObject.scroll({top: 0, left: 0});
// If `options.load` is not explicitly set to `false`, run handler.
if (!(options !== undefined && typeof options === 'object' && options.load !== undefined && options.load === false)) {
handleLoad();
}
})(window, document, options);
};
// Then, scroll down to it smoothly.
scrollTo(this.windowObject.location.hash, this.documentObject, this.windowObject);
});
};
}
const scrollTo = (hash, documentObject, windowObject) => {
const element = documentObject.querySelector(hash);
......@@ -118,16 +119,6 @@ const scrollTo = (hash, documentObject, windowObject) => {
windowObject.location.hash = hash;
}
});
/*
* @todo: Consider possibility of using Velocity or this native depending on offset from page top.
* Maybe the native one is better for shorter distances, Velocity is better for longer distances.
* Or maybe just work with the duration on Velocity would be better.
*/
// documentObject.querySelector('#' + hash).scrollIntoView({
// behavior: 'smooth'
// });
};
//Code for custom easing and helpers for custom easing
......
{
"name": "@grifart/smoothscroll",
"version": "0.3.1",
"version": "0.4.0",
"main": "index.js",
"private": true,
"dependencies": {
......
......@@ -8,18 +8,45 @@ As smoothscroll functionality is nice and more user-friendly, this library solve
Additionally this library comes with custom easing function registered by default which works as basic ease-in-out with one modification that it skips content if it is too long. This results in nicer transition between two parts of a page.
## Development
## Usage
Import library using ES6 `import` statement and call the smoothscroll function.
It accepts one optional parameter - an `options` object, which allows you to turn on/off some behaviour.
```javascript
SmoothScroll([options])
```
### Options
| Option | Value | Default value | Description |
| --- | --- | --- | --- |
| `load` | `true`/`false` | `true` | Causes smooth scroll to anchored element when the page is loaded.\*
| `interaction` | `true`/`false` | `true` | Causes smooth scroll on given element when user clicks on an `a` tag having `href` starting with `#` character.
\*Note: when the page load lasts more than 500 ms, load smooth scrolling is disable as it would lead to user-unfriendly behaviour like jumping on the page up and down.
Whole library consists only of one file - `index.js`. If you need to check how the smoothscrolling looks, see *Visual check* section of this readme.
Usage example with all options passed:
```javascript
SmoothScroll({
load: true,
interaction: true,
});
```
## Development
## Visual check
Whole library consists only of one file - `index.js`.
For checking a visual feeling of the smoothscroll funcionality, you can take advantage of a testing file `visual.html`.
You need to run build first to get it work with following commands:
If you need to check visually how the smooth scrolling behaviour acts like, you can take advantage of a testing file `visual.html` which has some lorem ipsum data and few of links to navigate through the content and test smooth scrolling.
To get it work, you need to run build first with following commands:
```bash
yarn install
gulp
yarn run gulp
```
And you are ready to view it by opening `visual.html` in your favourite browser. To track for changes, run `gulp watch` instead of `gulp`.
You can use `yarn run gulp watch` as well.
import SmoothScroll from './index';
const smoothScroll = new SmoothScroll(window, document);
smoothScroll.handleInteraction();
smoothScroll.handleLoad();
SmoothScroll({
load: true,
interaction: true,
});
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment