简体   繁体   中英

Disable scroll while waiting to for scroll selection animation to complete

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?

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>

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.

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/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM