This Blueprint is page navigation effect based on the Dribbble shot Stacked navigation by Ilya Kostin. The idea is to show a navigation when clicking on the menu button and transform all pages in 3D and move them to the bottom of the viewport. The next two pages are shown in the back of the current page as a stack. When clicking on a menu item, the respective page comes up.
这是基于Ilya Kostin的Dribbble shot堆叠导航的页面效果,这个效果是在点击菜单按钮时显示导航,并将所有页面转换为3D,并且把它们移动到视口的底部,接下来的两页作为一个堆栈显示在当前页面的后面,当点击菜单项时相应的页面会出现。
HTML
<!-- navigation -->
<nav class="pages-nav">
<div class="pages-nav__item"><a class="link link--page" href="#page-home">Home</a></div>
<div class="pages-nav__item"><a class="link link--page" href="#page-docu">Documentation</a></div>
<div class="pages-nav__item"><a class="link link--page" href="#page-manuals">Manuals</a></div>
<div class="pages-nav__item"><a class="link link--page" href="#page-software">Software</a></div>
<div class="pages-nav__item"><a class="link link--page" href="#page-custom">Customization & Settings</a></div>
<div class="pages-nav__item"><a class="link link--page" href="#page-training">Training</a></div>
<div class="pages-nav__item pages-nav__item--small"><a class="link link--page link--faded" href="#page-buy">Where to buy</a></div>
<div class="pages-nav__item pages-nav__item--small"><a class="link link--page link--faded" href="#page-blog">Blog & News</a></div>
<div class="pages-nav__item pages-nav__item--small"><a class="link link--page link--faded" href="#page-contact">Contact</a></div>
<div class="pages-nav__item pages-nav__item--social">
<a class="link link--social link--faded" href="#"><i class="fa fa-twitter"></i><span class="text-hidden">Twitter</span></a>
<a class="link link--social link--faded" href="#"><i class="fa fa-linkedin"></i><span class="text-hidden">LinkedIn</span></a>
<a class="link link--social link--faded" href="#"><i class="fa fa-facebook"></i><span class="text-hidden">Facebook</span></a>
<a class="link link--social link--faded" href="#"><i class="fa fa-youtube-play"></i><span class="text-hidden">YouTube</span></a>
</div>
</nav>
<!-- /navigation-->
<!-- pages stack -->
<div class="pages-stack">
<!-- page -->
<div class="page" id="page-home">
<!-- page content -->
</div>
<!-- /page -->
<div class="page" id="page-docu">
<!-- page content -->
</div>
<div class="page" id="page-manuals">
<!-- page content -->
</div>
<div class="page" id="page-software">
<!-- page content -->
</div>
<div class="page" id="page-custom">
<!-- page content -->
</div>
<div class="page" id="page-training">
<!-- page content -->
</div>
<div class="page" id="page-buy">
<!-- page content -->
</div>
<div class="page" id="page-blog">
<!-- page content -->
</div>
<div class="page" id="page-contact">
<!-- page content -->
</div>
</div>
<!-- /pages-stack -->
<button class="menu-button"><span>Menu</span></button>
<script src="js/classie.js"></script>
<script src="js/main.js"></script>
CSS
html.js,
.js body {
overflow: hidden;
height: 100vh;
}
/* Pages nav */
.pages-nav {
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
-webkit-justify-content: center;
justify-content: center;
-webkit-align-items: center;
align-items: center;
padding: 20px;
text-align: center;
background: #0e0f0f;
}
.js .pages-nav {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 50vh;
padding: 30px;
pointer-events: none;
opacity: 0;
background: transparent;
-webkit-transition: -webkit-transform 1.2s, opacity 1.2s;
transition: transform 1.2s, opacity 1.2s;
-webkit-transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
-webkit-transform: translate3d(0, 150px, 0);
transform: translate3d(0, 150px, 0);
}
.js .pages-nav--open {
pointer-events: auto;
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
.pages-nav__item {
width: 33%;
padding: 1em;
}
.js .pages-nav__item {
padding: 0 10%;
}
.pages-nav .pages-nav__item--social {
width: 100%;
opacity: 0;
-webkit-transition: -webkit-transform 1.2s, opacity 1.2s;
transition: transform 1.2s, opacity 1.2s;
-webkit-transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
-webkit-transform: translate3d(0, 20px, 0);
transform: translate3d(0, 20px, 0);
}
.pages-nav--open .pages-nav__item--social {
opacity: 1;
-webkit-transition-delay: 0.35s;
transition-delay: 0.35s;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
.link {
font-size: 0.85em;
font-weight: bold;
position: relative;
letter-spacing: 1px;
text-transform: uppercase;
}
.link:hover,
.link:focus {
color: #fff;
}
.link--page {
display: block;
color: #cecece;
}
.link--page:not(.link--faded)::before {
content: '';
position: absolute;
top: 100%;
left: 50%;
width: 30px;
height: 2px;
margin: 5px 0 0 -15px;
background: #fff;
-webkit-transition: -webkit-transform 0.3s;
transition: transform 0.3s;
-webkit-transform: scale3d(0, 1, 1);
transform: scale3d(0, 1, 1);
}
.link--page:hover:before {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
.link--faded {
color: #4f4f64;
}
.link--faded:hover,
.link--faded:focus {
color: #5c5edc;
}
.link--page.link--faded {
font-size: 0.65em;
}
.link--social {
font-size: 1.5em;
margin: 0 0.75em;
}
.text-hidden {
position: absolute;
display: block;
overflow: hidden;
width: 0;
height: 0;
color: transparent;
}
/* Pages stack */
.js .pages-stack {
z-index: 100;
pointer-events: none;
-webkit-perspective: 1200px;
perspective: 1200px;
-webkit-perspective-origin: 50% -50%;
perspective-origin: 50% -50%;
}
.js .page {
position: relative;
z-index: 5;
overflow: hidden;
width: 100%;
height: 100vh;
pointer-events: auto;
background: #2a2b30;
box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
}
.js .pages-stack--open .page {
cursor: pointer;
-webkit-transition: -webkit-transform 0.45s, opacity 0.45s;
transition: transform 0.45s, opacity 0.45s;
-webkit-transition-timing-function: cubic-bezier(0.6, 0, 0.4, 1);
transition-timing-function: cubic-bezier(0.6, 0, 0.4, 1);
}
.js .page--inactive {
position: absolute;
z-index: 0;
top: 0;
opacity: 0;
}
/* page content */
.info {
font-size: 1.25em;
max-width: 50%;
margin-top: 1.5em;
}
.poster {
position: absolute;
bottom: 4vh;
left: 60%;
max-width: 100%;
max-height: 80%;
}
/* Menu button */
.menu-button {
position: absolute;
z-index: 1000;
top: 30px;
left: 30px;
width: 30px;
height: 24px;
padding: 0;
cursor: pointer;
border: none;
outline: none;
background: transparent;
}
.no-js .menu-button {
display: none;
}
.menu-button::before,
.menu-button::after,
.menu-button span {
background: #5f656f;
}
.menu-button::before,
.menu-button::after {
content: '';
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 2px;
pointer-events: none;
-webkit-transition: -webkit-transform 0.25s;
transition: transform 0.25s;
-webkit-transform-origin: 50% 50%;
transform-origin: 50% 50%;
}
.menu-button span {
position: absolute;
left: 0;
overflow: hidden;
width: 100%;
height: 2px;
text-indent: 200%;
-webkit-transition: opacity 0.25s;
transition: opacity 0.25s;
}
.menu-button::before {
-webkit-transform: translate3d(0, -10px, 0) scale3d(0.8, 1, 1);
transform: translate3d(0, -10px, 0) scale3d(0.8, 1, 1);
}
.menu-button::after {
-webkit-transform: translate3d(0, 10px, 0) scale3d(0.8, 1, 1);
transform: translate3d(0, 10px, 0) scale3d(0.8, 1, 1);
}
.menu-button--open span {
opacity: 0;
}
.menu-button--open::before {
-webkit-transform: rotate3d(0, 0, 1, 45deg);
transform: rotate3d(0, 0, 1, 45deg);
}
.menu-button--open::after {
-webkit-transform: rotate3d(0, 0, 1, -45deg);
transform: rotate3d(0, 0, 1, -45deg);
}
@media screen and (max-width: 60em) {
.info {
max-width: 100%;
}
.poster {
position: relative;
top: auto;
left: auto;
display: block;
max-width: 100%;
max-height: 50vh;
margin: 0 0 0 50%;
}
.pages-nav__item {
width: 50%;
min-height: 20px;
}
.link--page {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.link--social {
margin: 0 0.1em;
}
}
@media screen and (max-width: 40em) {
.js .pages-nav {
display: block;
padding: 10px 20px 0 20px;
text-align: left;
}
.js .pages-nav__item {
width: 100%;
padding: 4px 0;
}
.js .pages-nav__item--small {
display: inline-block;
width: auto;
margin-right: 5px;
}
.pages-nav__item--social {
font-size: 0.9em;
}
.menu-button {
top: 15px;
right: 10px;
left: auto;
}
.info {
font-size: 0.85em;
}
.poster {
margin: 1em;
}
}
JavaScript
/**
* 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);
冒泡