Off-canvas Side Navigation With Page Transitions

Category: Javascript , Menu & Navigation , Recommended | February 7, 2018
Author:Kyle Brumm
Views Total:946 views
Official Page:Go to website
Last Update:February 7, 2018
License:MIT

Preview:

Off-canvas Side Navigation With Page Transitions

Description:

A modern sticky off-canvas sidebar navigation where the users are able to switch between page sections with a smooth transition effect by clicking nav links.

How to use it:

Create the off-canvas navigation that contains anchor links pointing to their page sections:

<nav class="nav">
  <ul class="nav__list">
    <li class="nav__item"><a href="#">Section 1</a></li>
    <li class="nav__item"><a href="#">Section 2</a></li>
    <li class="nav__item"><a href="#">Section 3</a></li>
    ...
  </ul>
</nav>

Create a trigger element to toggle the off-canvas navigation.

<div class="nav__bar">
  <a href="#" class="nav__trigger">
    <div class="bars"></div>
  </a>        
</div>

Create the sectioned content as follows:

<main class="main">

  <section class="content">

    <article class="article">
      <a href class="article__title">Section 1</a>
      <p class="article__content">
        Section 1 Content
      </p>
    </article>
      
    <article class="article">
      <a href class="article__title">Section 2</a>
      <p class="article__content">
        Section 2 Content
      </p>
    </article>

    <article class="article">
      <a href class="article__title">Section 3</a>
      <p class="article__content">
        Section 3 Content
      </p>
    </article>

    ...

  </section>
</main>

The primary CSS/CSS3 styles.

*, *:before, *:after {
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}

html {
  font-family: "Open Sans", Helvetica, arial, sans-serif;
  color: #333333;
  background-color: #eeeeee;
}

body.is-froze {
  overflow: hidden;
  width: 100vw;
  height: 100vh;
}

h1, h2, h3, h4, h5, h6 { font-family: "Raleway", "Open Sans", sans-serif; }

a {
  color: #333333;
  text-decoration: none;
}

img { max-width: 100%; }

a {
  -webkit-transition: color 0.3s ease-in-out;
  transition: color 0.3s ease-in-out;
}

a:hover { color: #7d87a8; }

.main {
  overflow: hidden;
  position: relative;
  width: 100%;
  width: calc(100% - 60px);
  height: 100vh;
  margin-left: 60px;
  background-color: #eeeeee;
  -webkit-transition: 0.55s cubic-bezier(0.645, 0.045, 0.355, 1);
  transition: 0.55s cubic-bezier(0.645, 0.045, 0.355, 1);
  -webkit-transform: scale(1) translate3d(0, 0, 0);
  transform: scale(1) translate3d(0, 0, 0);
  -webkit-clip-path: inset(0 0 0 0);
  clip-path: inset(0 0 0 0);
  will-change: width, height, opacity, transform, clip-path;
  z-index: 1;
}

.main.is-active {
  overflow: hidden;
  height: 100vh;
  width: 100vw;
  width: calc(100vw - 60px);
  pointer-events: none;
  opacity: 0.25;
  -webkit-transform: scale(0.9) translate3d(60%, 0, 0);
  transform: scale(0.9) translate3d(60%, 0, 0);
}

@media (min-width: 600px) {

.main.is-active {
  -webkit-transform: scale(0.9) translate3d(40%, 0, 0);
  transform: scale(0.9) translate3d(40%, 0, 0);
}
}

.main.is-transition-out {
  -webkit-clip-path: inset(0 0 0 100%);
  clip-path: inset(0 0 0 100%);
}

.article {
  padding: 1.5rem;
  position: relative;
}

@media (min-width: 600px) {

.article { padding: 6vmin; }
}

.article:not(:last-of-type):after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 1.5rem;
  width: 50px;
  height: 2px;
  background-color: #7d87a8;
}

@media (min-width: 600px) {

.article:not(:last-of-type):after { left: 6vmin; }
}

.article__title {
  display: block;
  position: relative;
  font-family: "Raleway", "Open Sans", sans-serif;
  font-size: 1.5rem;
  color: #191b22;
}
@media (min-width: 600px) {

.article__title { font-size: 3vmin; }
}

.article__title:hover { color: #7d87a8; }

.article__time {
  display: block;
  position: relative;
  text-transform: uppercase;
  font-size: 0.8rem;
  margin-top: 1rem;
}

@media (min-width: 600px) {

.article__time { font-size: 1.5vmin; }
}

.article__content {
  margin: 1rem 0 0;
  font-size: 1rem;
  line-height: 1.5;
}

@media (min-width: 600px) {

.article__content { font-size: 2vmin; }
}

.nav__bar {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  width: 60px;
  height: 100vh;
  border-right: 1px solid rgba(125, 135, 168, 0.25);
  background-color: #191b22;
  z-index: 99;
}

.nav__trigger {
  display: block;
  position: absolute;
  top: 50%;
  left: 16px;
  padding: 8px 0;
  margin-top: -10px;
  -webkit-transition: 0.2s ease-in-out;
  transition: 0.2s ease-in-out;
  z-index: 99;
}

.nav__trigger .bars { position: relative; }

.nav__trigger .bars, .nav__trigger .bars:before, .nav__trigger .bars:after {
  width: 28px;
  height: 4px;
  background-color: #7d87a8;
  -webkit-transition: 0.2s ease-in-out;
  transition: 0.2s ease-in-out;
  border-radius: 4px;
}

.nav__trigger .bars:before, .nav__trigger .bars:after {
  content: '';
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  will-change: transform;
}

.nav__trigger .bars:before {
  -webkit-transform: translateY(-8px);
  transform: translateY(-8px);
}

.nav__trigger .bars:after {
  -webkit-transform: translateY(8px);
  transform: translateY(8px);
}

.nav__trigger.is-active {
  -webkit-transform: rotate(-45deg);
  transform: rotate(-45deg);
}

.nav__trigger.is-active .bars:before, .nav__trigger.is-active .bars:after {
  -webkit-transform: translateX(0) rotate(-90deg);
  transform: translateX(0) rotate(-90deg);
}

.nav {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: #191b22;
  z-index: 0;
}

.nav__list {
  overflow: hidden;
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  margin: 0;
  padding-left: 60px;
  list-style: none;
  font-family: "Raleway", "Open Sans", sans-serif;
  -webkit-transform: translateY(-50%);
  transform: translateY(-50%);
}

.nav__list .nav__item { padding: 0.5rem 1rem; }

@media (min-width: 600px) {

.nav__list .nav__item {
  width: 33.3333333333%;
  padding: 0.5rem 1rem;
}
}

.nav__list a {
  display: inline-block;
  color: #7d87a8;
  font-size: 1rem;
  line-height: 1.5;
}

.nav__list a:hover { color: #b1b7cb; }

.nav__list a.is-active { color: #d2d5e1; }
@media (min-width: 600px) {

.nav__list a { font-size: 1.5rem; }
}

Load the necessary classList.js and smoothScroll libraries in the document.

<script src='https://cdnjs.cloudflare.com/ajax/libs/classlist/2014.01.31/classList.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/iamdustan-smoothscroll/0.4.0/smoothscroll.js'></script>

The main JavaScript to activate the off-canvas navigation & smooth page transition effects.

let navigation = {
    // Variables
    $navTrigger: document.querySelector('.nav__trigger'),
    $nav: document.querySelector('.nav'),
    $navItems: document.querySelectorAll('.nav__item a'),
    $main: document.querySelector('.main'),
    transitionEnd: 'webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend',
    isOpeningNav: false,

    init() {
        let self = this;

        // Reset overflow and height on load
        self.$main.style.overflow = 'auto';
        self.$main.style.height = 'auto';

        
        // Handle scroll events
        window.addEventListener('scroll', (e) => {
            if (window.scrollY == 0 && self.isOpeningNav) {
                self.isOpeningNav = false;
                
                // Add a small delay
                setTimeout(function() {
                    self.openNavigation();                    
                }, 150);
            }
        });

        // Handle .nav__trigger click event
        self.$navTrigger.addEventListener('click', (e) => {
            e.preventDefault();
            
            if (!self.$navTrigger.classList.contains('is-active')) {
                if (window.scrollY !== 0) {
                    // Scroll to top
                    window.scroll({ top: 0, left: 0, behavior: 'smooth' });

                    // Enable opening nav
                    self.isOpeningNav = true;                    
                } else {
                    self.openNavigation();
                }
            } else {
                self.closeNavigation();
            }
        });
                
        // Handle .nav__item click events
        self.$navItems.forEach((navLink) => {
            navLink.addEventListener('click', function(e) {
                e.preventDefault();
                
                // Remove is-active from all .nav__items
                self.$navItems.forEach((el) => {
                    el.classList.remove('is-active');
                });
                
                // Ad is-active to clicked .nav__item
                this.classList.add('is-active');
                
                // Transition the page
                self.transitionPage();
            });
        });
    },
    
    openNavigation() {
        let self = this;

        // .nav--trigger active
        self.$navTrigger.classList.add('is-active');

        // body froze
        document.body.classList.add('is-froze');

        // Remove old inline styles
        if (self.$main.style.removeProperty) {
            self.$main.style.removeProperty('overflow');
            self.$main.style.removeProperty('height');
        } else {
            self.$main.style.removeAttribute('overflow');
            self.$main.style.removeAttribute('height');
        }

        // .main active
        self.$main.classList.add('is-active');
    },
    
    closeNavigation() {
        let self = this;
        
        // .nav--trigger inactive
        self.$navTrigger.classList.remove('is-active');

        // .main inactive
        self.$main.classList.remove('is-active');
        self.$main.addEventListener('transitionend', (e) => {    
            if (e.propertyName == 'transform' && !self.$navTrigger.classList.contains('is-active')) {
                // Reset overflow and height
                self.$main.style.overflow = 'auto';
                self.$main.style.height = 'auto';

                // body unfroze
                document.body.classList.remove('is-froze');
            }
        });                    

        // no-csstransitions fallback
        if (document.documentElement.classList.contains('no-csstransitions')) {
            // .main inactive
            self.$main.classList.remove('is-active');

            // body unfroze
            document.body.classList.remove('is-froze');
        }
    },
    
    transitionPage() {
        let self = this;
        
        // .main transitioning
        self.$main.classList.add('is-transition-out');
        self.$main.addEventListener('transitionend', (e) => {    
            if (e.propertyName == 'clip-path') {
                if (self.$main.classList.contains('is-transition-in')) {
                    self.$main.classList.remove('is-transition-in');
                    self.$main.classList.remove('is-transition-out');
                    self.closeNavigation();
                }

                if (self.$main.classList.contains('is-transition-out')) {
                    self.$main.classList.remove('is-transition-out');
                    
                    // Add new content to .main
                    
                    setTimeout(function() {
                        self.$main.classList.add('is-transition-in');
                    }, 500);
                }
            }
        });
    }
}

navigation.init();

You Might Be Interested In:


Leave a Reply