Search code examples
javascripthorizontal-scrollingsmooth-scrolling

Horizontal Smooth Momentum Scrolling


THE PROBLEM: I need to apply some mouse wheel smooth horizontal scrolling to this layout: https://jsfiddle.net/38qLnzkh/.

ALTERNATIVE: I've found this Script that does exactly what I want but it seems to work only vertically: Butter.js. If you can make it work Horizontally it would probably solve all my problems.

IMPORTANT:

1. The Script should be disabled based on screen width and in touch devices.

2. It should accommodate a menu on top of everything like you seen in the fiddle.

Thank you in advance.

EDIT: In case it's not clear what I need, here are two examples with the effect I'm looking for:

https://nemesiscorporation.com/

https://www.tentwenty.me/about-us


MY LAYOUT:

HTML:

<main class="outer-wrapper">
    <div class="wrapper">
        <article class="section" id="a"><h2>01</h2></article>
        <article class="section" id="b"><h2>02</h2></article>
        <article class="section" id="c"><h2>03</h2></article>
        <article class="section" id="d"><h2>04</h2></article>
        <article class="section" id="e"><h2>05</h2></article>
        <article class="section" id="f"><h2>06</h2></article>
    </div>
</main>

CSS:

.outer-wrapper {
  width: auto;
  height: 100vw;
  transform: rotate(-90deg) translateX(-100vh);
  transform-origin: top left;
  overflow-y: scroll;
  overflow-x: hidden;
  position: absolute;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
::-webkit-scrollbar {
  display: none;
}

.wrapper {
  display: flex;
  flex-direction: row;
  width: auto;
  transform: rotate(90deg) translateY(-100vh);
  transform-origin: top left;
    margin: 0;
  padding: 0;
}

.section {
    color: #000;
  width: 100vw;
  height: 100vh;
}

Solution

  • I've published an API on github that can easily solve this problem, below you'll find the code to do what you want.

    Compared to yours I've just added the js code and the <script> in the HTML.

    If you want to know more about it, here you can find the documentation.

    EDIT
    Since the requirements have changed a little bit and the API has been updated I've modified the example below so that it better fits the question.

    Main changes to the answer:

    • Now the js is inside a init() method called onload
    • The css styles have been modified (transform:rotate brakes most of scrolling APIs)
    • The support for the navbar's smooth scroll has been added
    • The scrolling amount now depends on how much the user physically scrolls the mousewheel

    /* UPDATED 2022 ANSWER */
    function init() {
        /*
         * Variables preparation
         */
        let yourWrapperElement = document.getElementsByClassName("outer-wrapper")[0];
        let whateverEaseFunctionYouWant = remaningScrollDistance => { return remaningScrollDistance / 15 + 1 };
    
        //Added support for navbar menu's smooth scrolling
        uss.hrefSetup();
    
        /*
         * As you asked for, we only apply the custom scrolling for desktop devices
         * by using the "wheel" event instead of the "scroll" or "touchmove" events.
         */
        yourWrapperElement.addEventListener("wheel", event => {
          /*
           * We want to overwrite the default scrolling behaviour
           * of your outer-wrapper component.
           */
          event.preventDefault();
          event.stopPropagation();
          uss.scrollXBy(event.deltaY, yourWrapperElement, null, false);             
        }, {passive:false});
    
        /*
         * We apply a the custom ease function
         * which will be used whenever our component is scrolled by the API
         */
        uss.setXStepLengthCalculator(whateverEaseFunctionYouWant, yourWrapperElement);
    }
    body {
      margin: 0;
      padding: 0;
    }
    
    .outer-wrapper {
      width: auto;
      height: 100vh; /*  Changed to vh */
      width: 100vw; /* Added */
      /*transform: rotate(-90deg) translateX(-100vh); ROTATING containers brakes 90% of scrolling APIs
      transform-origin: top left;*/
      overflow-y: scroll;
      overflow-x: hidden;
      position: absolute;
      scrollbar-width: none;
      -ms-overflow-style: none;
      /*scroll-behavior: smooth; ISN'T NEEDED FOR MY API */
    }
    
    ::-webkit-scrollbar {
      display: none;
    }
    
    .wrapper {
      display: flex;
      flex-direction: row;
      /*width: auto; NOT NEEDED IF WE USE FLEX-SHRINK 0
      transform: rotate(90deg) translateY(-100vh); ROTATING containers brakes 90% of scrolling APIs
      transform-origin: top left;*/
      margin: 0; /* not really needed */
      padding: 0; /* not really needed */
    }
    
    .section {
      color: #000;
      flex-shrink: 0; /* ADDED insted of the width/height of the wrapper */
      width: 100vw;
      height: 100vh;
    }
    
    #a { background-color: #ccc; }
    #b { background-color: #fff; }
    #c { background-color: #ccc; }
    #d { background-color: #fff; }
    #e { background-color: #ccc; }
    #f { background-color: #fff; }
    
    
    h2 {
      text-align: center;
      font-size: 200px;
      margin: 0;
    }
    
    
    
    /* MENU  _________________________ */
    
    
    
    .logo {
      float: left;
    }
    
    nav {
      width: 100%;
    }
    
    /* HEADER */
    
    header {
      float: left;
      width: 100%;
      position: absolute;
      z-index: 9999;
    }
    
    
    /* HEADER LARGE */
    
    header.large {
      height: 50px;
    }
    
    header.large .logo {
      width: 225px;
      height: 50px;
      margin: 20px 0 0 20px;
      background: url('../images/logo-fireqa-green-500px.png');
      background-repeat: no-repeat;
      background-size: contain;
    
      transition: 0.7s all;
      -moz-transition: 0.7s all;
      -webkit-transition: 0.7s all;
      -o-transition: 0.7s all;
    }
    
    
    /* UNORDERED LIST */
    
    header.large ul {
      list-style: none;
      float: right;
      margin-right: 25px;
    }
    header.small ul {
      list-style: none;
      float: right;
      margin: 0;
    }
    
    header.large li {
      display: inline;
      float: left;
      list-style-position: inside;
      height: 50px;
      -webkit-transition: all ease 0.3s;
      -moz-transition: all ease 0.3s;
      transition: all 0.3s ease-in-out;
    }
    
    
    header.large li a {
      display: block;
      padding: 20px;
      color: #0E6245;
      text-decoration: none;
      font-family: 'Montserrat', 'arial', sans-serif;
      font-weight: 600 !important;
      letter-spacing: -1px;
      font-size: 25px;
    
      background-image: linear-gradient(#0E6245, #0E6245);
      background-position: 50% 80%;
      background-repeat: no-repeat;
      background-size: 0% 4px;
    
      -moz-transition: all 0.3s ease-in-out 0s;
      -ms-transition: all 0.3s ease-in-out 0s;
      -o-transition: all 0.3s ease-in-out 0s;
      -webkit-transition: all 0.3s ease-in-out 0s;
      transition: all 0.3s ease-in-out 0s;
    }
    
    header.large li a:hover, a:focus {
      background-size: 60% 4px;
    }
    <script src = "https://cdn.jsdelivr.net/npm/universalsmoothscroll@latest/universalsmoothscroll-min.js"></script>
    
    <body onload = init()>
    
    
    <main class="outer-wrapper">
        <div class="wrapper">
            <article class="section" id="a"><h2>01</h2></article>
            <article class="section" id="b"><h2>02</h2></article>
            <article class="section" id="c"><h2>03</h2></article>
            <article class="section" id="d"><h2>04</h2></article>
            <article class="section" id="e"><h2>05</h2></article>
            <article class="section" id="f"><h2>06</h2></article>
        </div>
    </main>
    
    
    <!-- MENU _____________________ -->
    
    
    <header class="large">
        <div class="container">
          <nav>
            <a><div class="logo"></div></a>
              <ul>
                <li><a href="#a">01</a></li>
                <li><a href="#b">02</a></li>
                <li><a href="#c">03</a></li>
                <li><a href="#d">04</a></li>
             </ul>
          </nav>
        </div>
      </header>
    
      </body>
    </html>