Horizontal Smooth Momentum Scrolling
Asked Answered
L

2

10

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;
}
Langlois answered 12/12, 2020 at 19:32 Comment(3)
Have a look at the mentioned lib: min30327.github.io/luxy.js You can set data-horizontal="1" for horizontal scrollingMeanie
Thanks for trying to help Josef. I think I've followed all the instructions but nothing seems to be happening... What am I doing wrong. Please have a look: Fiddle2Langlois
I'm sorry, I just saw that this animates horizontal movement while you scroll vertically (like the letters on the demo page). I'll find you something else. Sorry.Meanie
L
10

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>
Larner answered 14/12, 2020 at 11:26 Comment(7)
Thank you, Christian, for taking the time to present this solution. It works and It's of very easy implementation. This is very close to what I need. The fact that it's based on scrollDistance however doesn't allow users to pause the page exactly where they want. But it's a great solution nonetheless, If I can't find a better one I'll definitely use it. Much apreciated!Langlois
Hi David, based on the examples you provided i thought that you didn't need pixel-precision scrolling but if that's the case, the API i've provided here has 2 methods you could find helpful and they are: uss.scrollYTo() and uss.scrollIntoView(). The first one allows you to smoothly go to the exact y-axys coordinate of your scrollingElement (in this case the outer-wrapper) whereas the second one scrolls your container till the element you specified is visible (with an allignment that you may require) . I hope you may find this helpful in case you can't find anything better !Larner
Hi Christian, I do not need pixel precision, but it bothers me that the user can't position a section of the layout in the viewport where he wants it, which might leave part of that section out. That's my only issue, it's not the end of the World... I was just hoping for something more fluid like the examples I provided where the speed and amount of scrolling correlates with the usage of the mousewheel. Don't get me wrong though, I'm really happy to have your solution and will be very grateful to use it if nothing closer to what I need comes along. Much appreciated!!!Langlois
Hi Cristian, I was very happily implanting my project using your script when today I realized it doesn't work in Firefox. Any suggestions?Langlois
Hi David, all the API functions work correctly on firefox as far as i've tested, even the provided example. So the problem may be due to the fact that in the example i've used the event.deltaY to understand by how much the user has scrolled: this works fine but the problem is that chrome and firefox provide two different values for the same ammount of scroll. Chrome uses the scroll value x 100 (that's why i put event.deltaY / 100 ) meanwhile firefox uses the raw value (so the / 100 part messes things up). I've updated the code so that correctly works in firefox now !Larner
Sorry, you're right, the API functions did work on Firefox, it was the difference in rendering that was making it unusable. Thank you for your explanation. It performs a lot better in Firefox now, although it scrolls about 3 times the distance it scrolls in other browsers. A lot better still. I've noticed other scrolling scripts have poor performance in Firefox. If this can't be improved, could we just cancel the script in Firefox?Langlois
Hi David, i would suggest to create 2 different variables: minScrollDistance (the one in the example i've provided) and minScrollDistanceRaw which will be the minScrollDistance / A_NUMBER_OF_YOUR_CHOICE and then applying minScrollDistanceRaw instead of minScrollDistance in the else statement of the wheel eventListener. If you've already noticed that the scrolling in firefox is 3 times the scrolling you have in chrome then you already know that A_NUMBER_OF_YOUR_CHOICE is 3. I've updated the js code so that it's as clear as possibleLarner
M
0

There is a nice package called smooth-scrollbar.

I've adjusted your example. It disables smooth scrolling for mobile devices, but otherwise it's just calling the package. And I've cleaned up some CSS.

/** @see https://mcmap.net/q/42184/-what-39-s-the-best-way-to-detect-a-39-touch-screen-39-device-using-javascript */
function isTouchDevice() {
  return window.matchMedia("(pointer: coarse)").matches;
}

function initSmoothScrolling() {
  const options = {
    damping: 0.1,
    alwaysShowTracks: true
  };
  const elements = document.querySelectorAll(".smooth-scrollbar");
  for (const element of elements) {
    Scrollbar.init(element, options);
  }
}

if (!isTouchDevice()) {
  initSmoothScrolling();
}
body {
  margin: 0;
  padding: 0;
}

.smooth-scrollbar {
  overflow: auto;
}

.wrapper {
  display: flex;
  flex-direction: row;
}

.section {
  width: 100vw;
  height: 100vh;
  flex-shrink: 0;
}

.section:nth-child(odd) {
  background-color: #ccc;
}

.section:nth-child(even) {
  background-color: #fff;
}

h2 {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  font-size: 200px;
  margin: 0;
}
<main class="smooth-scrollbar">
  <div class="wrapper">
    <article class="section"><h2>01</h2></article>
    <article class="section"><h2>02</h2></article>
    <article class="section"><h2>03</h2></article>
    <article class="section"><h2>04</h2></article>
    <article class="section"><h2>05</h2></article>
    <article class="section"><h2>06</h2></article>
  </div>
</main>

<script src="https://cdnjs.cloudflare.com/ajax/libs/smooth-scrollbar/8.5.3/smooth-scrollbar.js"></script>
Meanie answered 13/12, 2020 at 6:59 Comment(3)
Hi Josef, I appreciate your help but I've tested your example in all major browsers and it doesn't scroll. :(Langlois
I've updated the original question with some examples of what I'm looking for...Langlois
Ok, smooth, smooth when you click on scrollbar trail, but I do not understand this answer... the post author asked applying mouse wheel smooth horizontal scrolling: Your snippet 1) shows something which scrolls also vertically, 2) and does not work with mouse wheel vertically...Silique

© 2022 - 2024 — McMap. All rights reserved.