js.anchor
Version:
Click and go to target. Pretty simple with only ~2KB and without dependencies.
167 lines (137 loc) • 4.4 kB
JavaScript
/*!
* anchor.js v1.1.0
* https://github.com/CezarLuiz0/anchor
*
* Licensed MIT © Cezar Luiz
*/
(function(window, document) {
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
var requestAnimFrame = (function(){
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); };
})();
var deepExtend = function(out) {
out = out || {};
for (var i = 1; i < arguments.length; i++) {
var obj = arguments[i];
if (!obj)
continue;
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object')
out[key] = deepExtend(out[key], obj[key]);
else
out[key] = obj[key];
}
}
}
return out;
};
var Anchor = function(options) {
var self = this;
var defaults = {
easings: {
easeInOutQuad: function (t, b, c, d) {
t /= d/2;
if (t < 1) {
return c/2*t*t + b;
}
t--;
return -c/2 * (t*(t-2) - 1) + b;
},
easeInCubic: function(t, b, c, d) {
var tc = (t/=d)*t*t;
return b+c*(tc);
},
inOutQuintic: function(t, b, c, d) {
var ts = (t/=d)*t,
tc = ts*t;
return b+c*(6*tc*ts + -15*ts*ts + 10*tc);
}
}
};
this.anchors = document.querySelectorAll('[data-anchor]');
this.opts = deepExtend({}, defaults, options);
// Exports
return {
init: function() {
self.bindAnchors();
}
};
};
Anchor.prototype = {
bindAnchors: function() {
var self = this;
[].slice.call(self.anchors).forEach(function(anchor) {
anchor.addEventListener('click', function(e) {
e.preventDefault();
var target = document.querySelector(this.getAttribute('data-anchor'));
var offset = self.getOffset(target) || 0;
var anchorOffset = this.getAttribute('data-anchor-offset') || 0;
var duration = this.getAttribute('data-anchor-duration') || 800;
var callback = this.getAttribute('data-anchor-callback');
var easing = this.getAttribute('data-anchor-easing') || 'easeInOutQuad';
self.scrollTo(
offset.top - parseInt(anchorOffset),
self.executeFunctionByName.bind(this, callback),
duration,
self.opts.easings[easing]
);
});
});
},
executeFunctionByName: function(functionName) {
if (!!functionName) {
// find object
var fn = window[functionName];
// is object a function?
if (typeof fn === "function") fn.apply(null);
}
},
getOffset: function(el) {
var left = 0;
var top = 0;
if ( el.offsetParent ) {
do {
left += el.offsetLeft
top += el.offsetTop
} while ( el = el.offsetParent )
}
return { 0: left, 1: top, left: left, top: top, length: 2 }
},
position: function() {
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop;
},
move: function(amount) {
document.documentElement.scrollTop = amount;
document.body.parentNode.scrollTop = amount;
document.body.scrollTop = amount;
},
scrollTo: function(to, callback, duration, easing) {
var self = this;
var start = self.position();
var change = to - start;
var currentTime = 0;
var increment = 20;
var duration = (typeof(duration) === 'undefined') ? 500 : duration;
var animateScroll = function() {
// increment the time
currentTime += increment;
// find the value with the quadratic in-out easing function
var val = easing(currentTime, start, change, duration);
// move the document.body
self.move(val);
// do the animation unless its over
if (currentTime < duration) {
requestAnimFrame(animateScroll);
} else {
if (callback && typeof(callback) === 'function') {
// the animation is done so lets callback
callback();
}
}
};
animateScroll();
}
};
window.Anchor = Anchor;
})(window, document);