1

I'm trying to make customize section-scroll where scroll event is disabled while animation is in motion. I tried with overflow hidden and other similar functions to apply while scroll animation is in progress but no luck.

JS fiddle example

CSS

.stop-scrolling {
  height: 100%;
  overflow: hidden;
}

JS example:

$('html').on('wheel', function(event) {
  if (event.originalEvent.deltaY > 0) {
//scroll down
    counter++;


    $('body').addClass('stop-scrolling');
    console.log("Start animacije");
    setTimeout(function () {
      $('body.stop-scrolling').removeClass('stop-scrolling');
      console.log("End animation");
    }, 800);



    if (!(counter > maxSections)) {
      $('html, body').animate({
        scrollTop: $( $("#sect-"+counter) ).offset().top
      }, 800);
    }

  } else {
//scroll up
    counter--;


    $('body').addClass('stop-scrolling');
    console.log("Start animation");
    setTimeout(function () {
      $('body.stop-scrolling').removeClass('stop-scrolling');
      console.log("End animation");
    }, 800);


    if (!(counter < 1)) {
      $('html, body').animate({
        scrollTop: $( $("#sect-"+counter) ).offset().top
      }, 800);
    }

  }

  if (counter <= 0) {
    counter = 1;
  }
  else if (counter >= 3) {
    counter = maxSections;
  }

  console.log(counter);
});

If you scroll while animation is in progress, scroll will continue until you reach end of sections.

Is possible to disable scroll event while animation is in progress?

Umutambyi Gad
  • 4,082
  • 3
  • 18
  • 39
Slasher
  • 568
  • 7
  • 29

3 Answers3

2

Instead of using CSS, you can use your script to block scroll events.

You can use the onComplete parameter of the .animate method to run a callback after animation ends.

With this, you can use a flag variable to determine whether or not the page is scrolling.

The whole process would be something like:

  1. Animation started.
  2. Flag animating = true.
  3. Block scrolling.
  4. Animation ended.
  5. Flag animating = false.
  6. Unblock scrolling.

Here's a minimal and reproducible example. It may have some bugs, but it should solve your main problem.

$(document).ready(function(){

  'use strict';
  
  // Flag to decide whether or not scroll is allowed.
  let animating = false;

  $(window).on('scroll', (e) => {
    if (animating){
      // Block scroll
      e.preventDefault();
    }
  });
  
  $('.section').on('wheel', e => {
    e.stopPropagation();
  
    // Do not run this function again when animation is happening.
    if (animating){
      return;
    }
  
    const $target = $(e.target);
    const isScrollDown = e.originalEvent.deltaY > 0;

    // Choose whether to scroll to next or previous section.
    const $targetSection = isScrollDown ? $target.next('.section') : $target.prev('.section');
    // Check whether or not there is a target
    const hasTargetSection = $targetSection.length > 0;

    // Only animate when there is a target.
    if (hasTargetSection){
      // Flag animation start
      animating = true;
      $('html, body').animate(
        // Animation properties
        {
          scrollTop: $targetSection.offset().top
        },
        // Animation duration
        800,
        // Function to run after animation ends
        () => {
          // Flag animation ended
          animating = false;
        }
      );
    }
  });

});
html, body, #app {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}

.section {
  width: 100%;
  height: 100%;
  font-size: 3em;
  display: flex;
  justify-content: center;
  align-items: center;
}

.a {
  background-color: #f008;
}

.b {
  background-color: #0f08;
}

.c {
  background-color: #00f8;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="app">
  <div class="section a">Section A</div>
  <div class="section b">Section B</div>
  <div class="section c">Section C</div>
</div>
yqlim
  • 6,898
  • 3
  • 19
  • 43
2

Here's simple fix with less code modification.

Just declare a variable that represents scrolling state. And using this state you can ignore incoming scroll events if currently scrolling.

let scrolling = false;

$('html').on('wheel', function(event) {
  if (scrolling) {
    return;
  } else {
    scrolling = true;
    setTimeout(function () {
      scrolling = false;
    }, 800 /* animation duration */);
  }

  ...

Here's final fiddle link.

Using setTimeout function you can reset scrolling state to false so, new events will be received.

TheMisir
  • 4,083
  • 1
  • 27
  • 37
2

What you are asking for is called throttling. I recommend you read this link to understand it better.

So to fix your code, you only need to enable throttling for your wheel event listener.

Like this:

let throttle = (fn, timedelay) => {
  let pass = true;
  return function () {
    if (pass) {
      setTimeout(() => {
        fn.apply(this, arguments);
        pass = true;
      }, timedelay);

      pass = false;
    }
  };
};

let scrollFunction = throttle(function(event) {
  // your code
}, 200); // 200 miliseconds sounds good

$('html').on('wheel', scrollFunction);

Here is a working code: https://jsfiddle.net/d1fcL5en/

Vahid
  • 6,639
  • 5
  • 37
  • 61