/** * main.js * http://www.codrops.com * * Licensed under the MIT license. * http://www.opensource.org/licenses/mit-license.php * * Copyright 2015, Codrops * http://www.codrops.com */ ;(function(window) { 'use strict'; var support = { transitions: Modernizr.csstransitions }, // transition end event name transEndEventNames = { 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd', 'msTransition': 'MSTransitionEnd', 'transition': 'transitionend' }, transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ], onEndTransition = function( el, callback ) { var onEndCallbackFn = function( ev ) { if( support.transitions ) { if( ev.target != this ) return; this.removeEventListener( transEndEventName, onEndCallbackFn ); } if( callback && typeof callback === 'function' ) { callback.call(this); } }; if( support.transitions ) { el.addEventListener( transEndEventName, onEndCallbackFn ); } else { onEndCallbackFn(); } }, // the pages wrapper stack = document.querySelector('.pages-stack'), // the page elements pages = [].slice.call(stack.children), // total number of page elements pagesTotal = pages.length, // index of current page current = 0, // menu button menuCtrl = document.querySelector('button.menu-button'), // the navigation wrapper nav = document.querySelector('.pages-nav'), // the menu nav items navItems = [].slice.call(nav.querySelectorAll('.link--page')), // check if menu is open isMenuOpen = false; function init() { buildStack(); initEvents(); } function buildStack() { var stackPagesIdxs = getStackPagesIdxs(); // set z-index, opacity, initial transforms to pages and add class page--inactive to all except the current one for(var i = 0; i < pagesTotal; ++i) { var page = pages[i], posIdx = stackPagesIdxs.indexOf(i); if( current !== i ) { classie.add(page, 'page--inactive'); if( posIdx !== -1 ) { // visible pages in the stack page.style.WebkitTransform = 'translate3d(0,100%,0)'; page.style.transform = 'translate3d(0,100%,0)'; } else { // invisible pages in the stack page.style.WebkitTransform = 'translate3d(0,75%,-300px)'; page.style.transform = 'translate3d(0,75%,-300px)'; } } else { classie.remove(page, 'page--inactive'); } page.style.zIndex = i < current ? parseInt(current - i) : parseInt(pagesTotal + current - i); if( posIdx !== -1 ) { page.style.opacity = parseFloat(1 - 0.1 * posIdx); } else { page.style.opacity = 0; } } } // event binding function initEvents() { // menu button click menuCtrl.addEventListener('click', toggleMenu); // navigation menu clicks navItems.forEach(function(item) { // which page to open? var pageid = item.getAttribute('href').slice(1); item.addEventListener('click', function(ev) { ev.preventDefault(); openPage(pageid); }); }); // clicking on a page when the menu is open triggers the menu to close again and open the clicked page pages.forEach(function(page) { var pageid = page.getAttribute('id'); page.addEventListener('click', function(ev) { if( isMenuOpen ) { ev.preventDefault(); openPage(pageid); } }); }); // keyboard navigation events document.addEventListener( 'keydown', function( ev ) { if( !isMenuOpen ) return; var keyCode = ev.keyCode || ev.which; if( keyCode === 27 ) { closeMenu(); } } ); } // toggle menu fn function toggleMenu() { if( isMenuOpen ) { closeMenu(); } else { openMenu(); isMenuOpen = true; } } // opens the menu function openMenu() { // toggle the menu button classie.add(menuCtrl, 'menu-button--open') // stack gets the class "pages-stack--open" to add the transitions classie.add(stack, 'pages-stack--open'); // reveal the menu classie.add(nav, 'pages-nav--open'); // now set the page transforms var stackPagesIdxs = getStackPagesIdxs(); for(var i = 0, len = stackPagesIdxs.length; i < len; ++i) { var page = pages[stackPagesIdxs[i]]; page.style.WebkitTransform = 'translate3d(0, 75%, ' + parseInt(-1 * 200 - 50*i) + 'px)'; // -200px, -230px, -260px page.style.transform = 'translate3d(0, 75%, ' + parseInt(-1 * 200 - 50*i) + 'px)'; } } // closes the menu function closeMenu() { // same as opening the current page again openPage(); } // opens a page function openPage(id) { var futurePage = id ? document.getElementById(id) : pages[current], futureCurrent = pages.indexOf(futurePage), stackPagesIdxs = getStackPagesIdxs(futureCurrent); // set transforms for the new current page futurePage.style.WebkitTransform = 'translate3d(0, 0, 0)'; futurePage.style.transform = 'translate3d(0, 0, 0)'; futurePage.style.opacity = 1; // set transforms for the other items in the stack for(var i = 0, len = stackPagesIdxs.length; i < len; ++i) { var page = pages[stackPagesIdxs[i]]; page.style.WebkitTransform = 'translate3d(0,100%,0)'; page.style.transform = 'translate3d(0,100%,0)'; } // set current if( id ) { current = futureCurrent; } // close menu.. classie.remove(menuCtrl, 'menu-button--open'); classie.remove(nav, 'pages-nav--open'); onEndTransition(futurePage, function() { classie.remove(stack, 'pages-stack--open'); // reorganize stack buildStack(); isMenuOpen = false; }); } // gets the current stack pages indexes. If any of them is the excludePage then this one is not part of the returned array function getStackPagesIdxs(excludePageIdx) { var nextStackPageIdx = current + 1 < pagesTotal ? current + 1 : 0, nextStackPageIdx_2 = current + 2 < pagesTotal ? current + 2 : 1, idxs = [], excludeIdx = excludePageIdx || -1; if( excludePageIdx != current ) { idxs.push(current); } if( excludePageIdx != nextStackPageIdx ) { idxs.push(nextStackPageIdx); } if( excludePageIdx != nextStackPageIdx_2 ) { idxs.push(nextStackPageIdx_2); } return idxs; } init(); })(window);