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 1:

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

2021 Update
With the 3.0 update this solution becomes slighlty inacurate so I've updated the code so that any future reader will have a proper answer.

/* NEW 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);
}


/* OLD ANSWER
function init() {
     //
     // Variables preparation
     //
    let yourWrapperElement = document.getElementsByClassName("outer-wrapper")[0];
    let minScrollDistance = window.innerWidth / 5; //Adjust the scroll sensitivity as you like
    let minScrollDistanceRaw = minScrollDistance / 3;
    let whateverEaseFunctionYouWant = remaningScrollDistance => { return remaningScrollDistance / 15 + 1 };
    let previousScrollDirection = undefined;
    let currentScrollDirection = undefined;

    //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();
       //
       // In this new section we check if the scrolling has changed currentScrollDirection
       // and if so we stop the scrolling on the exact spot the user decided
       //
      currentScrollDirection = Math.sign(event.deltaY);
      if(currentScrollDirection !== previousScrollDirection) {
        previousScrollDirection = currentScrollDirection;
        uss.stopScrollingX(yourWrapperElement);
      }

      //The scrolling amount is now influenced by how much the user scrolls
      if(Math.abs(event.deltaY) >= 100) //For browsers that use multiples of 100 as deltaY
          uss.scrollXBy(event.deltaY / 100 * minScrollDistance, yourWrapperElement, null, true);
      else //For the browsers that use the raw scroll input as deltaY
          uss.scrollXBy(event.deltaY * minScrollDistanceRaw, yourWrapperElement, null, true);     
    }, {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>