UNPKG

avvo-styleguide

Version:
127 lines (99 loc) 3.62 kB
import Avvo from '../util/avvo-object' const $ = global.jQuery export class SmoothScroller { static get VERSION() { return '1.0.0' } static get DEFAULTS() { return Object.freeze({ anchorSelector: 'a[href^="#"]', fastSpeed: 400, slowSpeed: 600, adjustScrollCoordinate: undefined, // callback fn(targetY, $target) }) } constructor(el, options) { this.$element = $(el) this.$element.data('ui.smoothScroller', this) this.settings = $.extend({}, SmoothScroller.DEFAULTS, this.$element.data(), options) this.$element.on('click.ui.smoothScroller', this.settings.anchorSelector, event => this.onClick(event)) } destroy() { this.$element.off('click.ui.smoothScroller') this.$element.removeData('ui.smoothScroller') Avvo.ui.smoothScroller = undefined } onClick(event) { if (!this.eventShouldTriggerScroll(event)) return event.preventDefault() this.scrollTo(event.currentTarget.getAttribute('href')) } // @param selector (String, DOM node, jQuery object) // @param scrollSpeed (Int) // animation speed, in ms. Defaults to `slowSpeed` or `fastSpeed` depending // on distance to target. // @return (Promise) scrollTo(selector, scrollSpeed) { const $target = this.getScrollTarget(selector) const targetY = this.getScrollCoordinate($target) const distance = Math.abs($(window).scrollTop() - targetY) let speed = scrollSpeed if (typeof speed === 'undefined') { speed = (distance < window.screen.availHeight) ? this.settings.slowSpeed : this.settings.fastSpeed } const sharedEventData = { target: $target[0], targetY, distance, speed } this.$element.trigger('scrollstart.ui.smoothScroller', sharedEventData) return $('html,body') .stop() .animate({ scrollTop: targetY }, { duration: speed, easing: 'swing', }) .promise() .done(() => { if (typeof selector === 'string' && selector.indexOf('#') === 0) { global.history.replaceState(null, null, selector) } this.$element.trigger('scrollend.ui.smoothScroller', sharedEventData) }) } // @param selector String, DOM node, jQuery object // @return (Int) the desired Y coordinate to scroll to, in pixels getScrollCoordinate(selector) { const $target = $(selector) let targetY = $target.offset().top - parseInt($target.css('margin-top'), 10) if (this.settings.adjustScrollCoordinate) { targetY = this.settings.adjustScrollCoordinate(targetY, $target[0]) } return targetY } // @param selector String, DOM node, jQuery object // @return (jQuery) getScrollTarget(selector) { if (selector === '#') return $('body') let $target = $(selector) if (!$target.length && selector.indexOf('#') === 0) { $target = $(`a[name='${selector.slice(1)}']`) } return $target } // @return (Bool) whether the event should trigger a smooth scroll eventShouldTriggerScroll(event) { const href = event.currentTarget.getAttribute('href') if (event.isDefaultPrevented()) return false if (href === '#') return true if (!document.querySelector(`[id='${href.slice(1)}'], a[name='${href.slice(1)}']`)) return false return true } } export function init() { if (Avvo.ui.smoothScroller) { if (window.console) { console.warn('Avvo.ui.smoothScroller already defined; canceling smoothScroller.init()') // eslint-disable-line } return } Avvo.ui.smoothScroller = new SmoothScroller(document) }