UNPKG

moveto

Version:

A lightweight scroll animation javascript library without any dependency.

229 lines (187 loc) 5.96 kB
/*! * MoveTo - A lightweight scroll animation javascript library without any dependency. * Version 1.8.2 (28-06-2019 14:30) * Licensed under MIT * Copyright 2019 Hasan Aydoğdu <hsnaydd@gmail.com> */ "use strict"; var MoveTo = function () { /** * Defaults * @type {object} */ var defaults = { tolerance: 0, duration: 800, easing: 'easeOutQuart', container: window, callback: function callback() {} }; /** * easeOutQuart Easing Function * @param {number} t - current time * @param {number} b - start value * @param {number} c - change in value * @param {number} d - duration * @return {number} - calculated value */ function easeOutQuart(t, b, c, d) { t /= d; t--; return -c * (t * t * t * t - 1) + b; } /** * Merge two object * * @param {object} obj1 * @param {object} obj2 * @return {object} merged object */ function mergeObject(obj1, obj2) { var obj3 = {}; Object.keys(obj1).forEach(function (propertyName) { obj3[propertyName] = obj1[propertyName]; }); Object.keys(obj2).forEach(function (propertyName) { obj3[propertyName] = obj2[propertyName]; }); return obj3; } ; /** * Converts camel case to kebab case * @param {string} val the value to be converted * @return {string} the converted value */ function kebabCase(val) { return val.replace(/([A-Z])/g, function ($1) { return '-' + $1.toLowerCase(); }); } ; /** * Count a number of item scrolled top * @param {Window|HTMLElement} container * @return {number} */ function countScrollTop(container) { if (container instanceof HTMLElement) { return container.scrollTop; } return container.pageYOffset; } ; /** * MoveTo Constructor * @param {object} options Options * @param {object} easeFunctions Custom ease functions */ function MoveTo() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var easeFunctions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; this.options = mergeObject(defaults, options); this.easeFunctions = mergeObject({ easeOutQuart: easeOutQuart }, easeFunctions); } /** * Register a dom element as trigger * @param {HTMLElement} dom Dom trigger element * @param {function} callback Callback function * @return {function|void} unregister function */ MoveTo.prototype.registerTrigger = function (dom, callback) { var _this = this; if (!dom) { return; } var href = dom.getAttribute('href') || dom.getAttribute('data-target'); // The element to be scrolled var target = href && href !== '#' ? document.getElementById(href.substring(1)) : document.body; var options = mergeObject(this.options, _getOptionsFromTriggerDom(dom, this.options)); if (typeof callback === 'function') { options.callback = callback; } var listener = function listener(e) { e.preventDefault(); _this.move(target, options); }; dom.addEventListener('click', listener, false); return function () { return dom.removeEventListener('click', listener, false); }; }; /** * Move * Scrolls to given element by using easeOutQuart function * @param {HTMLElement|number} target Target element to be scrolled or target position * @param {object} options Custom options */ MoveTo.prototype.move = function (target) { var _this2 = this; var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (target !== 0 && !target) { return; } options = mergeObject(this.options, options); var distance = typeof target === 'number' ? target : target.getBoundingClientRect().top; var from = countScrollTop(options.container); var startTime = null; var lastYOffset; distance -= options.tolerance; // rAF loop var loop = function loop(currentTime) { var currentYOffset = countScrollTop(_this2.options.container); if (!startTime) { // To starts time from 1, we subtracted 1 from current time // If time starts from 1 The first loop will not do anything, // because easing value will be zero startTime = currentTime - 1; } var timeElapsed = currentTime - startTime; if (lastYOffset) { if (distance > 0 && lastYOffset > currentYOffset || distance < 0 && lastYOffset < currentYOffset) { return options.callback(target); } } lastYOffset = currentYOffset; var val = _this2.easeFunctions[options.easing](timeElapsed, from, distance, options.duration); options.container.scroll(0, val); if (timeElapsed < options.duration) { window.requestAnimationFrame(loop); } else { options.container.scroll(0, distance + from); options.callback(target); } }; window.requestAnimationFrame(loop); }; /** * Adds custom ease function * @param {string} name Ease function name * @param {function} fn Ease function */ MoveTo.prototype.addEaseFunction = function (name, fn) { this.easeFunctions[name] = fn; }; /** * Returns options which created from trigger dom element * @param {HTMLElement} dom Trigger dom element * @param {object} options The instance's options * @return {object} The options which created from trigger dom element */ function _getOptionsFromTriggerDom(dom, options) { var domOptions = {}; Object.keys(options).forEach(function (key) { var value = dom.getAttribute("data-mt-".concat(kebabCase(key))); if (value) { domOptions[key] = isNaN(value) ? value : parseInt(value, 10); } }); return domOptions; } return MoveTo; }(); if (typeof module !== 'undefined') { module.exports = MoveTo; } else { window.MoveTo = MoveTo; }