diff --git a/js/elevator.js b/js/elevator.js new file mode 100644 index 00000000..0ea66b5a --- /dev/null +++ b/js/elevator.js @@ -0,0 +1,184 @@ +/*! + * Elevator.js + * + * MIT licensed + * Copyright (C) 2015 Tim Holman, http://tholman.com + */ + +/********************************************* + * Elevator.js + *********************************************/ + +var Elevator = (function() { + + 'use strict'; + + // Elements + var body = null; + + // Scroll vars + var animation = null; + var duration = null; // ms + var customDuration = false; + var startTime = null; + var startPosition = null; + + var mainAudio; + var endAudio; + + var elevating = false; + + /** + * Utils + */ + + // Soft object augmentation + function extend( target, source ) { + for ( var key in source ) { + if ( !( key in target ) ) { + target[ key ] = source[ key ]; + } + } + return target; + }; + + // Thanks Mr Penner - http://robertpenner.com/easing/ + function easeInOutQuad( 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; + }; + + /** + * Main + */ + + // Time is passed through requestAnimationFrame, what a world! + function animateLoop( time ) { + if (!startTime) { + startTime = time; + } + + var timeSoFar = time - startTime; + var easedPosition = easeInOutQuad(timeSoFar, startPosition, -startPosition, duration); + + window.scrollTo(0, easedPosition); + + if( timeSoFar < duration ) { + animation = requestAnimationFrame(animateLoop); + } else { + animationFinished(); + } + }; + +// ELEVATE! +// / +// ____ +// .' '=====<0 +// |======| +// |======| +// [IIIIII[\--() +// |_______| +// C O O O D +// C O O O D +// C O O O D +// C__O__O__O__D +// [_____________] + function elevate() { + + if( elevating ) { + return; + } + + elevating = true; + startPosition = (document.documentElement.scrollTop || body.scrollTop); + + // No custom duration set, so we travel at pixels per millisecond. (0.75px per ms) + if( !customDuration ) { + duration = (startPosition * 1.5); + } + + requestAnimationFrame( animateLoop ); + + // Start music! + if( mainAudio ) { + mainAudio.play(); + } + } + + function resetPositions() { + startTime = null; + startPosition = null; + elevating = false; + } + + function animationFinished() { + + resetPositions(); + + // Stop music! + if( mainAudio ) { + mainAudio.pause(); + mainAudio.currentTime = 0; + } + + if( endAudio ) { + endAudio.play(); + } + } + + function onWindowBlur() { + + // If animating, go straight to the top. And play no more music. + if( elevating ) { + + cancelAnimationFrame( animation ); + resetPositions(); + + if( mainAudio ) { + mainAudio.pause(); + mainAudio.currentTime = 0; + } + + window.scrollTo(0, 0); + } + } + + //@TODO: Does this need tap bindings too? + function bindElevateToElement( element ) { + element.addEventListener('click', elevate, false); + } + + function main( options ) { + + // Bind to element click event, if need be. + body = document.body; + + if( options.element ) { + bindElevateToElement( options.element ); + } + + if( options.duration ) { + customDuration = true; + duration = options.duration; + } + + if( options.mainAudio ) { + mainAudio = new Audio( options.mainAudio ); + mainAudio.setAttribute( 'preload', 'true' ); //@TODO: Option to not preload audio. + mainAudio.setAttribute( 'loop', 'true' ); + } + + if( options.endAudio ) { + endAudio = new Audio( options.endAudio ); + endAudio.setAttribute( 'preload', 'true' ); + } + + window.addEventListener('blur', onWindowBlur, false); + } + + return extend(main, { + elevate: elevate + }); +})();