mirror of
https://github.com/sourcey/spectacle.git
synced 2024-09-11 05:25:36 +03:00
258 lines
7.0 KiB
JavaScript
258 lines
7.0 KiB
JavaScript
'use strict';
|
|
|
|
|
|
import $ from 'jquery';
|
|
import { GetYoDigits } from './foundation.util.core';
|
|
import { Plugin } from './foundation.plugin';
|
|
import { SmoothScroll } from './foundation.smoothScroll';
|
|
|
|
/**
|
|
* Magellan module.
|
|
* @module foundation.magellan
|
|
* @requires foundation.smoothScroll
|
|
*/
|
|
|
|
class Magellan extends Plugin {
|
|
/**
|
|
* Creates a new instance of Magellan.
|
|
* @class
|
|
* @name Magellan
|
|
* @fires Magellan#init
|
|
* @param {Object} element - jQuery object to add the trigger to.
|
|
* @param {Object} options - Overrides to the default plugin settings.
|
|
*/
|
|
_setup(element, options) {
|
|
this.$element = element;
|
|
this.options = $.extend({}, Magellan.defaults, this.$element.data(), options);
|
|
this.className = 'Magellan'; // ie9 back compat
|
|
|
|
this._init();
|
|
this.calcPoints();
|
|
}
|
|
|
|
/**
|
|
* Initializes the Magellan plugin and calls functions to get equalizer functioning on load.
|
|
* @private
|
|
*/
|
|
_init() {
|
|
var id = this.$element[0].id || GetYoDigits(6, 'magellan');
|
|
var _this = this;
|
|
this.$targets = $('[data-magellan-target]');
|
|
this.$links = this.$element.find('a');
|
|
this.$element.attr({
|
|
'data-resize': id,
|
|
'data-scroll': id,
|
|
'id': id
|
|
});
|
|
this.$active = $();
|
|
this.scrollPos = parseInt(window.pageYOffset, 10);
|
|
|
|
this._events();
|
|
}
|
|
|
|
/**
|
|
* Calculates an array of pixel values that are the demarcation lines between locations on the page.
|
|
* Can be invoked if new elements are added or the size of a location changes.
|
|
* @function
|
|
*/
|
|
calcPoints() {
|
|
var _this = this,
|
|
body = document.body,
|
|
html = document.documentElement;
|
|
|
|
this.points = [];
|
|
this.winHeight = Math.round(Math.max(window.innerHeight, html.clientHeight));
|
|
this.docHeight = Math.round(Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight));
|
|
|
|
this.$targets.each(function(){
|
|
var $tar = $(this),
|
|
pt = Math.round($tar.offset().top - _this.options.threshold);
|
|
$tar.targetPoint = pt;
|
|
_this.points.push(pt);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initializes events for Magellan.
|
|
* @private
|
|
*/
|
|
_events() {
|
|
var _this = this,
|
|
$body = $('html, body'),
|
|
opts = {
|
|
duration: _this.options.animationDuration,
|
|
easing: _this.options.animationEasing
|
|
};
|
|
$(window).one('load', function(){
|
|
if(_this.options.deepLinking){
|
|
if(location.hash){
|
|
_this.scrollToLoc(location.hash);
|
|
}
|
|
}
|
|
_this.calcPoints();
|
|
_this._updateActive();
|
|
});
|
|
|
|
this.$element.on({
|
|
'resizeme.zf.trigger': this.reflow.bind(this),
|
|
'scrollme.zf.trigger': this._updateActive.bind(this)
|
|
}).on('click.zf.magellan', 'a[href^="#"]', function(e) {
|
|
e.preventDefault();
|
|
var arrival = this.getAttribute('href');
|
|
_this.scrollToLoc(arrival);
|
|
});
|
|
|
|
this._deepLinkScroll = function(e) {
|
|
if(_this.options.deepLinking) {
|
|
_this.scrollToLoc(window.location.hash);
|
|
}
|
|
};
|
|
|
|
$(window).on('popstate', this._deepLinkScroll);
|
|
}
|
|
|
|
/**
|
|
* Function to scroll to a given location on the page.
|
|
* @param {String} loc - a properly formatted jQuery id selector. Example: '#foo'
|
|
* @function
|
|
*/
|
|
scrollToLoc(loc) {
|
|
this._inTransition = true;
|
|
var _this = this;
|
|
|
|
var options = {
|
|
animationEasing: this.options.animationEasing,
|
|
animationDuration: this.options.animationDuration,
|
|
threshold: this.options.threshold,
|
|
offset: this.options.offset
|
|
};
|
|
|
|
SmoothScroll.scrollToLoc(loc, options, function() {
|
|
_this._inTransition = false;
|
|
_this._updateActive();
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Calls necessary functions to update Magellan upon DOM change
|
|
* @function
|
|
*/
|
|
reflow() {
|
|
this.calcPoints();
|
|
this._updateActive();
|
|
}
|
|
|
|
/**
|
|
* Updates the visibility of an active location link, and updates the url hash for the page, if deepLinking enabled.
|
|
* @private
|
|
* @function
|
|
* @fires Magellan#update
|
|
*/
|
|
_updateActive(/*evt, elem, scrollPos*/) {
|
|
if(this._inTransition) {return;}
|
|
var winPos = /*scrollPos ||*/ parseInt(window.pageYOffset, 10),
|
|
curIdx;
|
|
|
|
if(winPos + this.winHeight === this.docHeight){ curIdx = this.points.length - 1; }
|
|
else if(winPos < this.points[0]){ curIdx = undefined; }
|
|
else{
|
|
var isDown = this.scrollPos < winPos,
|
|
_this = this,
|
|
curVisible = this.points.filter(function(p, i){
|
|
return isDown ? p - _this.options.offset <= winPos : p - _this.options.offset - _this.options.threshold <= winPos;
|
|
});
|
|
curIdx = curVisible.length ? curVisible.length - 1 : 0;
|
|
}
|
|
|
|
this.$active.removeClass(this.options.activeClass);
|
|
this.$active = this.$links.filter('[href="#' + this.$targets.eq(curIdx).data('magellan-target') + '"]').addClass(this.options.activeClass);
|
|
|
|
if(this.options.deepLinking){
|
|
var hash = "";
|
|
if(curIdx != undefined){
|
|
hash = this.$active[0].getAttribute('href');
|
|
}
|
|
if(hash !== window.location.hash) {
|
|
if(window.history.pushState){
|
|
window.history.pushState(null, null, hash);
|
|
}else{
|
|
window.location.hash = hash;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.scrollPos = winPos;
|
|
/**
|
|
* Fires when magellan is finished updating to the new active element.
|
|
* @event Magellan#update
|
|
*/
|
|
this.$element.trigger('update.zf.magellan', [this.$active]);
|
|
}
|
|
|
|
/**
|
|
* Destroys an instance of Magellan and resets the url of the window.
|
|
* @function
|
|
*/
|
|
_destroy() {
|
|
this.$element.off('.zf.trigger .zf.magellan')
|
|
.find(`.${this.options.activeClass}`).removeClass(this.options.activeClass);
|
|
|
|
if(this.options.deepLinking){
|
|
var hash = this.$active[0].getAttribute('href');
|
|
window.location.hash.replace(hash, '');
|
|
}
|
|
$(window).off('popstate', this._deepLinkScroll);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Default settings for plugin
|
|
*/
|
|
Magellan.defaults = {
|
|
/**
|
|
* Amount of time, in ms, the animated scrolling should take between locations.
|
|
* @option
|
|
* @type {number}
|
|
* @default 500
|
|
*/
|
|
animationDuration: 500,
|
|
/**
|
|
* Animation style to use when scrolling between locations. Can be `'swing'` or `'linear'`.
|
|
* @option
|
|
* @type {string}
|
|
* @default 'linear'
|
|
* @see {@link https://api.jquery.com/animate|Jquery animate}
|
|
*/
|
|
animationEasing: 'linear',
|
|
/**
|
|
* Number of pixels to use as a marker for location changes.
|
|
* @option
|
|
* @type {number}
|
|
* @default 50
|
|
*/
|
|
threshold: 50,
|
|
/**
|
|
* Class applied to the active locations link on the magellan container.
|
|
* @option
|
|
* @type {string}
|
|
* @default 'is-active'
|
|
*/
|
|
activeClass: 'is-active',
|
|
/**
|
|
* Allows the script to manipulate the url of the current page, and if supported, alter the history.
|
|
* @option
|
|
* @type {boolean}
|
|
* @default false
|
|
*/
|
|
deepLinking: false,
|
|
/**
|
|
* Number of pixels to offset the scroll of the page on item click if using a sticky nav bar.
|
|
* @option
|
|
* @type {number}
|
|
* @default 0
|
|
*/
|
|
offset: 0
|
|
}
|
|
|
|
export {Magellan};
|