/* scrolldeck - jQuery scrolldeck to create a vertically scrolling presentation deck by John Polacek (@johnpolacek) Dual licensed under MIT and GPL. */ (function($) { $.scrolldeck = function(options) { // VARS var currIndex = 0, buttons, slides, scrollpoints = [], sections = [], windowHeight = $(window).height(), i; var defaults = { buttons: '.nav-button', slides: '.slide', duration: 600, easing: 'easeInOutExpo', offset: 0 }; // INIT var scrolldeck = this; scrolldeck.settings = {}; var init = function() { scrolldeck.settings = $.extend({}, defaults, options); buttons = $(scrolldeck.settings.buttons); slides = $(scrolldeck.settings.slides); scrolldeck.controller = $.scrollorama({blocks:slides, offset:scrolldeck.settings.offset}); // add animations with scrollorama var anim; // ANIMATE INS for (i=0; i<$('.animate-in').length; i++) { anim = $('.animate-in').eq(i); switch (anim.attr('data-animation')) { case 'fly-in-left': anim .parent().css('overflow','hidden'); scrolldeck.controller.animate(anim, { delay: windowHeight/2, duration: windowHeight/2, property:'left', start:-1200 }); break; case 'fly-in-right': anim .parent().css('overflow','hidden'); scrolldeck.controller.animate(anim, { delay: windowHeight/2, duration: windowHeight/2, property:'right', start:-1200 }); break; case 'space-in': scrolldeck.controller.animate(anim, { delay: windowHeight*0.8, duration: windowHeight*0.2, property:'letter-spacing', start:40 }); scrolldeck.controller.animate(anim, { delay: windowHeight*0.8, duration: windowHeight*0.2, property:'opacity', start:0 }); break; case 'fade-in': scrolldeck.controller.animate(anim, { delay: windowHeight/2, duration: windowHeight/2, property:'opacity', start:0 }); break; default: scrolldeck.controller.animate(anim, { delay: windowHeight/2, duration: windowHeight/2, property:'opacity', start:0 }); } } // ANIMATE BUILDS for (i=0; i<$('.animate-build').length; i++) { anim = $('.animate-build').eq(i); switch (anim.attr('data-animation')) { case 'fly-in-left': anim.parent().css('overflow','hidden'); scrolldeck.controller.animate(anim, { delay: (anim.attr('data-build')-1)*400, duration: 400, property:'left', start:-1200, pin:true }); break; case 'fly-in-right': anim.parent().css('overflow','hidden'); scrolldeck.controller.animate(anim, { delay: (anim.attr('data-build')-1)*400, duration: 400, property:'right', start:-1200, pin:true }); break; case 'space-in': scrolldeck.controller.animate(anim, { delay: (anim.attr('data-build')-1)*400, duration: 400, property:'letter-spacing', start:40, pin:true }); scrolldeck.controller.animate(anim, { delay: (anim.attr('data-build')-1)*400, duration: 400, property:'opacity', start:0, pin:true }); break; case 'fade-in': scrolldeck.controller.animate(anim, { delay: (anim.attr('data-build')-1)*400, duration: 400, property:'opacity', start:0, pin:true }); break; default: scrolldeck.controller.animate(anim, { delay: (anim.attr('data-build')-1)*400, duration: 400, property:'opacity', start:0, pin:true }); } } // set slide and animation scrollpoints scrollpoints = scrolldeck.controller.getScrollpoints(); // if nav buttons, create array of section header slide indexes for (i=0; i<buttons.length;i++) sections.push(slides.index($($(buttons[i]).attr('href')))); // event handler for updating current slide index and current nav button scrolldeck.controller.onBlockChange(function() { // get slide index currIndex = scrolldeck.controller.blockIndex; // then update nav updateNav(); }); // Nav button click event buttons.on('click', function(e) { e.preventDefault(); var slide = $($(this).attr('href')); currIndex = slide.index(); scrollToSlide(slide); }); // Keyboard events $(document).on('keydown', function(e){ // up/left arrow = scroll up if ((e.keyCode == 37 || e.keyCode == 38) && currIndex !== 0) { scrollToSlide(getPrevScrollpoint()); } // down/right arrow, space = scroll down else if ((e.keyCode == 39 || e.keyCode == 32 || e.keyCode == 40) && currIndex != slides.length-1) { scrollToSlide(getNextScrollpoint()); } }); // if slides are images, assign height to auto for proportional scaling for (i=0; i<slides.length; i++) { var el = slides.eq(i); if (el.prop('tagName').toUpperCase() === 'IMG') { el.css('height','auto'); } } // if last slide is shorter than height of window, increase height var lastSlide = slides.eq(slides.length-1); if (lastSlide.outerHeight() < $(window).height()) { lastSlide.height(lastSlide.height()+$(window).height()-lastSlide.outerHeight()); } updateNav(); }; // PRIVATE FUNCTIONS function updateNav() { if (buttons) { buttons.removeClass('current'); var currSection = -1; for (i=0; i<sections.length;i++) { if (currIndex >= sections[i]) { currSection = i; } } if (currSection != -1) { buttons.eq(currSection).addClass('current'); } } } function scrollToSlide(slide) { $(window)._scrollable().stop(); $(window).scrollTo(slide, { duration: scrolldeck.settings.duration, easing: scrolldeck.settings.easing, offset: scrolldeck.settings.offset }); } function getNextScrollpoint() { return getScrollpoint(2); } function getPrevScrollpoint() { return getScrollpoint(-1); } function getScrollpoint(n) { var scrollTop = $(window).scrollTop(); // make temp dup scrollpoints array var points = scrollpoints.slice(0); // add current scroll position to new temp array points.push(scrollTop); // do sort to find nearest scrollpoint points.sort(function(a,b){return a - b;}); return points[points.indexOf(scrollTop)+n]; } // INIT init(); }; })(jQuery);