Skip to content

Instantly share code, notes, and snippets.

@issunboshi
Created April 30, 2015 07:33
Show Gist options
  • Save issunboshi/0e87e5c1a0872d81aa59 to your computer and use it in GitHub Desktop.
Save issunboshi/0e87e5c1a0872d81aa59 to your computer and use it in GitHub Desktop.
Parallax library without jQuery dependency and using ES6 modules
import {indexOf} from 'helpers/indexOf';
import {addClass} from 'helpers/addClass';
import {removeClass} from 'helpers/removeClass';
var parallax = function() {
// @todo change to Class and establish these vars in constructor. Will enable Class to be generic
/* Globals
-------------------------------------------------- */
var PROPERTIES = ['translateX', 'translateY', 'opacity', 'rotate', 'scale'],
fakeWindow = document.body,
body = document.querySelectorAll('body')[0],
wrappers = [],
currentWrapper = null,
scrollTimeoutID = 0,
bodyHeight = 0,
windowHeight = 0,
windowWidth = 0,
prevKeyframesDurations = 0,
scrollTop = 0,
prevScrollTop = 0,
relativeScrollTop = 0,
currentKeyframe = 0,
keyframes = [
{
'wrapper' : '#intro',
'duration' : '100%',
'animations' : [
{
'selector' : '.name',
'translateY' : -140,
'opacity' : 0
},
{
'selector' : '.byline',
'translateY' : -110,
'opacity' : 0
},
{
'selector' : '.twitter',
'opacity' : [1, 0]
}
]
},
{
'wrapper' : '#explosion',
'duration' : '150%',
'animations' : [
{
'selector' : '.explosion-byline',
'translateY' : '-25%',
'opacity' : [0, 1.75] // hack to accelerate opacity speed
},
{
'selector' : '#domExplosionList',
'translateY' : '-70%',
'opacity' : [0, 1] // hack to accelrate opacity speed
}
]
},
{
'wrapper' : '#explosion',
'duration' : '150%',
'animations' : [
{
'selector' : '.dei-1',
'translateY' : '-15%',
'translateX' : '-10%',
'opacity' : [1, 0],
'scale' : 2,
},
{
'selector' : '.dei-2',
'translateY' : '-5%',
'translateX' : '-4%',
'opacity' : [1, 0] // hack to decelrate opacity speed
},
{
'selector' : '.dei-3',
'translateY' : '-9%',
'translateX' : '2%',
'opacity' : [1, 0], // hack to accelrate opacity speed
'scale' : 1.2,
},
{
'selector' : '.dei-4',
'translateY' : '-17%',
'translateX' : '8%',
'opacity' : [1, 0], // hack to accelrate opacity speed
'scale' : 1.5,
},
{
'selector' : '.dei-5',
'translateY' : '-2%',
'translateX' : '-15%',
'opacity' : [1, 0],
'scale' : 2,
},
{
'selector' : '.dei-6',
'translateY' : '-1%',
'translateX' : '-7%',
'opacity' : [1, 0], // hack to decelrate opacity speed
'scale' : 1.2,
},
{
'selector' : '.dei-7',
'translateY' : '-4%',
'translateX' : '2%',
'opacity' : [1, 0], // hack to accelrate opacity speed
'scale' : 1.1,
},
{
'selector' : '.dei-8',
'translateY' : '-3%',
'translateX' : '12%',
'opacity' : [1, 0], // hack to accelrate opacity speed
'scale' : 1.8,
},
{
'selector' : '.dei-9',
'translateY' : '3%',
'translateX' : '-12%',
'opacity' : [1, 0],
'scale' : 1.5,
},
{
'selector' : '.dei-10',
'translateY' : '5%',
'translateX' : '-4%',
'opacity' : [1, 0] // hack to decelrate opacity speed
},
{
'selector' : '.dei-11',
'translateY' : '8%',
'translateX' : '6%',
'opacity' : [1, 0], // hack to accelrate opacity speed
'scale' : 1.4,
},
{
'selector' : '.dei-12',
'translateY' : '1%',
'translateX' : '20%',
'opacity' : [1, 0], // hack to accelrate opacity speed
'scale' : 1.9,
},
{
'selector' : '.dei-13',
'translateY' : '8%',
'translateX' : '-12%',
'opacity' : [1, 0],
'scale' : 1.8,
},
{
'selector' : '.dei-14',
'translateY' : '4%',
'translateX' : '-3%',
'opacity' : [1, 0], // hack to decelrate opacity speed
'scale' : 1.3,
},
{
'selector' : '.dei-15',
'translateY' : '14%',
'translateX' : '5%',
'opacity' : [1, 0], // hack to accelrate opacity speed
'scale' : 1.7,
},
{
'selector' : '.dei-16',
'translateY' : '6%',
'translateX' : '9%',
'opacity' : [1, 0], // hack to accelrate opacity speed
'scale' : 2,
}
]
},
{
'wrapper' : '#explosion',
'duration' : '100%',
'animations' : [
{
'selector' : '.explosion-byline',
'translateY' : ['-25%', '-40%'],
'opacity' : [1, 0] // hack to accelrate opacity speed
}
]
},
{
'wrapper' : '#images',
'duration' : '150%',
'animations' : [
{
'selector' : '.images-byline',
'translateY' : '-25%',
'opacity' : [0, 1.75] // hack to accelrate opacity speed
},
{
'selector' : '#mediumHomepage',
'translateY' : '-90%'
},
{
'selector' : '.iphone',
'translateY' : '-66%'
}
]
},
{
'wrapper' : '#images',
'duration' : '75%',
'animations' : []
},
{
'wrapper' : '#images',
'duration' : '150%',
'animations' : [
{
'selector' : '.images-byline',
'translateY' : ['-25%', '-25%'],
'scale' : 0.7,
'opacity' : [1.75, -0.75] // hack to accelrate opacity speed
},
{
'selector' : '.images-byline-2',
'opacity' : [0, 1],
'translateY' : '-15%'
},
{
'selector' : '#mediumHomepage',
'translateY' : ['-90%', '-90%'],
'scale' : 0.8,
'opacity' : -0.75
},
{
'selector' : '.iphone',
'translateY' : ['-66%', '-90%'],
'translateX' : '-2%',
'rotate' : -90,
'scale' : 1.3
},
{
'selector' : '#medium-profile-iphone',
'scale' : 0.9,
'translateX' : '20%',
},
{
'selector' : '#davegamache-dot-com',
'scale' : [0.5, 1]
}
]
},
{
'wrapper' : '#images',
'duration' : '40%',
'animations' : []
},
{
'wrapper' : '#images',
'duration' : '150%',
'animations' : [
{
'selector' : '.images-byline-2',
'translateY' : ['-15%', '50%'],
'opacity' : [1, -2]
},
{
'selector' : '.iphone',
'translateY' : ['-90%', '5%'],
'translateX' : ['-2%', '-2%'],
'rotate' : [-90, -90],
'scale' : [1.3, 1.3]
},
{
'selector' : '#medium-profile-iphone',
'translateX' : ['20%', '20%']
},
{
'selector' : '#davegamache-dot-com',
'scale' : [1, 1]
}
]
},
{
'wrapper' : '#links',
'duration' : '100%',
'animations' : [
{
'selector' : '#links',
'opacity' : [0, 2],
'scale' : [0.8, 1]
},
{
'selector' : '.twitter',
'opacity' : [0, 1]
}
]
},
{
'duration' : '100%',
'animations' : []
}];
/* Construction
-------------------------------------------------- */
var init = function() {
var scrollIntervalID = setInterval(updatePage, 80);
setupValues();
};
var setupValues = function() {
scrollTop = fakeWindow.scrollTop;
windowHeight = window.innerHeight;
windowWidth = window.innerWidth;
convertAllPropsToPx();
buildPage();
};
var buildPage = function() {
var i, j, k;
for(i=0; i<keyframes.length; i++) { // loop keyframes
bodyHeight += keyframes[i].duration;
if(indexOf(wrappers, keyframes[i].wrapper) == -1) {
wrappers.push(keyframes[i].wrapper);
}
for(j=0; j<keyframes[i].animations.length; j++) { // loop animations
/**
* @todo refactor to remove function inside of loop
*/
Object.keys(keyframes[i].animations[j]).forEach(function(key) { // loop properties
var value = keyframes[i].animations[j][key];
if(key !== 'selector' && value instanceof Array === false) {
var valueSet = [];
valueSet.push(getDefaultPropertyValue(key), value);
value = valueSet;
}
keyframes[i].animations[j][key] = value;
});
}
}
body.style.height = bodyHeight + 'px';
window.scroll(0, 0);
currentWrapper = wrappers[0];
var currentWrapperDom = document.querySelectorAll(currentWrapper)[0];
addClass(currentWrapperDom, 'show');
removeClass(currentWrapperDom, 'hide');
};
var convertAllPropsToPx = function() {
var i, j, k;
for(i=0; i<keyframes.length; i++) { // loop keyframes
keyframes[i].duration = convertPercentToPx(keyframes[i].duration, 'y');
for(j=0; j<keyframes[i].animations.length; j++) { // loop animations
Object.keys(keyframes[i].animations[j]).forEach(function(key) { // loop properties
var value = keyframes[i].animations[j][key];
if(key !== 'selector') {
if(value instanceof Array) { // if its an array
for(k=0; k<value.length; k++) { // if value in array is %
if(typeof value[k] === "string") {
if(key === 'translateY') {
value[k] = convertPercentToPx(value[k], 'y');
}
else {
value[k] = convertPercentToPx(value[k], 'x');
}
}
}
}
else {
if(typeof value === "string") { // if single value is a %
if(key === 'translateY') {
value = convertPercentToPx(value, 'y');
}
else {
value = convertPercentToPx(value, 'x');
}
}
}
keyframes[i].animations[j][key] = value;
}
});
}
}
};
var getDefaultPropertyValue = function(property) {
switch (property) {
case 'translateX':
return 0;
case 'translateY':
return 0;
case 'scale':
return 1;
case 'rotate':
return 0;
case 'opacity':
return 1;
default:
return null;
}
};
/* Animation/Scrolling
-------------------------------------------------- */
var updatePage = function() {
window.requestAnimationFrame(function() {
setScrollTops();
if(prevScrollTop != scrollTop) {
animateElements();
setKeyframe();
prevScrollTop = scrollTop;
}
});
};
var setScrollTops = function() {
scrollTop = fakeWindow.scrollTop;
relativeScrollTop = scrollTop - prevKeyframesDurations;
};
var animateElements = function() {
var animation, translateY, translateX, scale, rotate, opacity;
for(var i=0; i<keyframes[currentKeyframe].animations.length; i++) {
animation = keyframes[currentKeyframe].animations[i];
translateY = calcPropValue(animation, 'translateY');
translateX = calcPropValue(animation, 'translateX');
scale = calcPropValue(animation, 'scale');
rotate = calcPropValue(animation, 'rotate');
opacity = calcPropValue(animation, 'opacity');
var animationEl = document.querySelectorAll(animation.selector)[0];
if(animationEl) {
animationEl.style.transform = 'translate3d(' + translateX +'px, ' + translateY + 'px, 0) scale('+ scale +') rotate('+ rotate +'deg)';
animationEl.style.opacity = opacity;
}
}
};
var calcPropValue = function(animation, property) {
var value = animation[property];
if(value) {
value = easeInOutQuad(relativeScrollTop, value[0], (value[1]-value[0]), keyframes[currentKeyframe].duration);
}
else {
value = getDefaultPropertyValue(property);
}
// value = +value.toFixed(2)
// TEMPORARILY REMOVED CAUSE SCALE DOESN'T WORK WITHA AGRESSIVE ROUNDING LIKE THIS
return value;
};
var easeInOutQuad = function (t, b, c, d) {
//sinusoadial in and out
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
};
var setKeyframe = function() {
if(scrollTop > (keyframes[currentKeyframe].duration + prevKeyframesDurations)) {
prevKeyframesDurations += keyframes[currentKeyframe].duration;
currentKeyframe++;
showCurrentWrappers();
}
else if(scrollTop < prevKeyframesDurations) {
currentKeyframe--;
prevKeyframesDurations -= keyframes[currentKeyframe].duration;
showCurrentWrappers();
}
};
var showCurrentWrappers = function() {
var i;
if(keyframes[currentKeyframe].wrapper != currentWrapper) {
var currentWrapperDom = document.querySelectorAll(currentWrapper)[0],
currentKeyframeWrapperDom = document.querySelectorAll(keyframes[currentKeyframe].wrapper)[0];
/**
* @todo Refactor to standard show/hide functions
*/
addClass(currentWrapperDom, 'hide');
removeClass(currentWrapperDom, 'show');
addClass(currentKeyframeWrapperDom, 'show');
removeClass(currentKeyframeWrapperDom, 'hide');
currentWrapper = keyframes[currentKeyframe].wrapper;
}
};
/* Helpers
-------------------------------------------------- */
var convertPercentToPx = function(value, axis) {
if(typeof value === "string" && value.match(/%/g)) {
if(axis === 'y') value = (parseFloat(value) / 100) * windowHeight;
if(axis === 'x') value = (parseFloat(value) / 100) * windowWidth;
}
return value;
};
init();
};
export {parallax};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment