avvo-styleguide
Version:
Avvo styleguide
127 lines (99 loc) • 3.62 kB
JavaScript
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)
}