Detect Intentional Top and Bottom extra scroll

℡╲_俬逩灬. 提交于 2021-02-07 12:27:47

问题


I'm trying to detect with javascript an intentional extra top/bottom scroll. $(window).scrollTop() and window.pageYOffset are not useful since they stop at Top 0 and I would like to reach something like top -X. For bottom let's suppose my document's height is 500, bottom would be like bottom 5XX.

EDIT: An example code could be:

$(window).scroll(function(){

  if(intentionalScrollTop){
    // Do something
  }else if(intentionalScrollDown){
    // Do something
  }

});

Gif example:


回答1:


What I understood from your question was not only to "detect an overscroll"...
But also use it to create an animation like you show in your question.

I made a solution using a wrapper div, as I commented 2 days ago.

You can see it in CodePen or the snippet below.

$(document).ready(function(){
	    
    var at_Top=true;
    var at_Bottom=false;
	var previous_scrolled;
	var triggerTime=0;
    var scroll_dir=false;   // false=>Down true=>up
	var scrolled_bottom = $("body").height() - $(window).height();
	var animationDelay = 300;
	var animationTimeout = 350;		//Max delay between 2 triggers is 1 sec.
									//So keep this value under 400ms
									//Because one complete animation is 300ms+350ms+300ms.
									//To have longer animation delays, add time to the triggerDelay
	
	var triggerDelay=0;	// You can add more delay to allow the next trigger (in seconds).
	
    $(window).scroll(function(){
        var scrolled=$(window).scrollTop();

        // Reached the top?
        if(scrolled==0){
            at_Top=true;
        }else{
            at_Top=false;
        }

        // Reached the bottom?
        if(scrolled==scrolled_bottom){
            at_Bottom=true;
        }else{
            at_Bottom=false;
        }
        
        // Scroll direction
		if( $(this).scrollTop() > previous_scrolled ){
			scroll_dir=false;  //scroll down
		}else{
			scroll_dir=true;  //scroll up
		}
		
		// Keep previous scrollTop position in memory
		previous_scrolled = $(this).scrollTop();
		
		animationTrigger();
    });

	function animationTrigger(){
        if(at_Top && scroll_dir){
            console.log("Scrolling when at top.");
			$("#wrapper").stop().animate({"margin-top":"3em"},animationDelay);
			setTimeout(function(){
				$("#wrapper").stop().animate({"margin-top":0},animationDelay);
			},animationTimeout);
			clearTimeout(clearConsole);
			var clearConsole = setTimeout(function(){
				console.clear();
			},3000);
        }
        if(at_Bottom && !scroll_dir){
            console.log("Scrolling when at bottom.")
			$("#header").stop().animate({"height":0},animationDelay);
			$("#footer-spacer").stop().animate({"height":"3em"},animationDelay);
			setTimeout(function(){
				$("#header").stop().animate({"height":"3em"},animationDelay);
				$("#footer-spacer").stop().animate({"height":0},animationDelay);
			},animationTimeout);
			clearTimeout(clearConsole);
			var clearConsole = setTimeout(function(){
				console.clear();
			},3000);
        }
    }
	
    // KEYBOARD ARROWS UP/DOWN AND PAGE UP/DOWN SCROLLING
    $(window).on("keydown",function(e){
        //console.log(e.which);
        if( (e.which==38) || (e.which==33) ){    // Arrow up or Page up
            scroll_dir=true;
        }
        if( (e.which==40) || (e.which==34) ){    // Arrow down or Page down
            scroll_dir=false;
        }
		
		// Limit triggers to 1 per second... Because when holding a key down for long, it triggers too fast...
		var thisSecond = new Date().getSeconds()
        if( (triggerTime != thisSecond) || (triggerTime < (thisSecond - triggerDelay) ) ){
            animationTrigger();
            triggerTime=thisSecond;
        }
    })

    // WHEEL SCROLLING
    // Inspired from this SO answer: http://stackoverflow.com/a/7309786/2159528

    //Firefox
    $(window).bind('DOMMouseScroll', function(e){
        var scrolled2=$(window).scrollTop();
        
        if(e.originalEvent.detail > 0) {
            scroll_dir=false;  //scroll down
            //console.log("down");
        }else {
            scroll_dir=true;   //scroll up
            //console.log("up");
        }
        
		// Limit triggers to 1 per second... Because wheel turns quite fast.
        var thisSecond = new Date().getSeconds()
        if( (triggerTime != thisSecond) || (triggerTime < (thisSecond - triggerDelay) ) ){
            animationTrigger();
            triggerTime=thisSecond;
        }
    });

    //IE, Opera, Safari
    $(window).bind('mousewheel', function(e){
		
        if(e.originalEvent.wheelDelta < 0) {
            scroll_dir=false;  //scroll down
        }else {
            scroll_dir=true;   //scroll up
        }
        
        // Limit triggers to 1 per second... Because wheel turns quite fast.
        var thisSecond = new Date().getSeconds()
        if( (triggerTime != thisSecond) || (triggerTime < (thisSecond - triggerDelay) ) ){
            animationTrigger();
            triggerTime=thisSecond;
        }
    });

});	// End Document.ready
body,wrapper{
    padding:0;
    margin:0;
    height:100;
}
#page{
    height:1500px;
    width:100%;
}
#header,#footer{
    height:3em;
    padding:0.5em;
    background-color:cyan;
}
#content{
    height:calc(100% - 8em);    /* -8em for header and footer... (height: 3em + padding: 2x 0,5em) */
    padding:0 0.5em;
    overflow:hidden;
}
#footer-spacer{
    height:0px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrapper">
    <div id="page">
        <div id="header">
            This is the page's top
        </div>
        <div id="content">
            <h1>Scroll this page to see the overscroll effect at top and bottom</h1>
            <br>
            <ul>
                <li>using the mouse wheel</li>
                <li>keyboard arrows</li>
                <li>keyboard page up/down</li>
            </ul>
            <br>
            <br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
            Content...<br>
        </div>
        
        <div id="footer">
            This is the page's bottom
        </div>
        <div id="footer-spacer"></div>
    </div>
</div>

To detect a scroll attempt when the page is at top or bottom is on thing...
Using it to set an animation like you show is another.

Approache for the top:
The wrapper's margin is animated from 0em to 3em, pushing the header and content down.
So the page "looks" like overscrolled.

Approache for the bottom:
This was a challenge...
Can't do the same as for the top with the wrapper's margin-bottom because it works... But below the viewport, which is not really what we want.

So in this case, I defined the "content" as height:calc(100% - 8em) (header and footer both have height:3em and padding:0.5em) just to make sure that wrapper is 100% filled. The empty div to animate is under the footer... When its height passes from 0em to 3em, it creates the overscroll "illusion" by pushing the footer up.

Note that the header retracts in the same time, in order to free space. At this point, the header isn't visible, so why not?

This script works when dragging the scrollbar, spinning the mousewheel and hitting 1 of the 4 "usual" keys on the keyboard (arrows and page up/down).

I leaved a lot of console.log() that you can use to explore how it works and improve the animation and make it your taste.
;)




回答2:


There is actually exists a library named overscroll but unfortunately this only works in Safari because Chrome has removed support for overscroll numbers.

So the only way for me to do it is to listen for mouse wheel event, check if current scroll position is on top or on the bottom of the page and then if scroll destination matches over scroll zone return true.

Keep in mind, this will fire event even if a system doesn't support over the scroll. So if you want to make it work only if true over scroll happens make sure to check if user's OS is macOS(navigator.platform === 'MacIntel').

document.getElementById("content").innerHTML = new Array(60).fill(0).map(function() {
    return '<div>Sample text</div>';
   }).join('');

document.onwheel = function (event) {
  if(intentionalScrollTop(event)) {
    console.log('overscrolled top');
  } else if(intentionalScrollBottom(event)) {
    console.log('overscrolled bottom');
  }
}
  
function intentionalScrollTop (event) {
  return document.body.scrollTop === 0 && event.deltaY < 0;
}
  
function intentionalScrollBottom (event) {
  return (window.innerHeight + window.scrollY) >= document.body.offsetHeight && event.deltaY > 0;
}
<div id="content"></div>



回答3:


Thanks to @Louys and @Konstantin I found some clues to get my answer.

To detect the scroll event with cross browser support I used this reference, which creates an event listener addWheelListener( elem, callback, useCapture ).

Then using scrollPos I can detect whether the scroll is at top/bottom edge or none and with e.deltaY and maxScrollTop or maxScrollBottom I can trigger the Extra scroll message.

$(document).ready(function(){

  var canvasHeight    = ($("body").height() - $(window).height()).toFixed(0),
      elem            = document.getElementsByTagName("BODY")[0],
      maxScrollTop    = -200,
      maxScrollBottom = 200,
      scrollTop       = false,
      scrollBottom    = false;

  addWheelListener( elem, function( e ) {  

    var scrollPos = $(window).scrollTop();

    // Is Scroll at Top or Bottom edge?
    if(scrollPos === 0 || scrollPos === parseInt(canvasHeight)){
      if(e.deltaY < -1){
        if(e.deltaY < maxScrollTop){
          $('#message').text('Extra Scroll Top');
          console.log('Extra Scroll Top');
        }

        // This can be removed if you dont need to detect the first scroll top.
        if(!scrollTop){
          scrollTop = true;
          $('#message').text('Scroll Top');
          console.log('Scroll Top');
        }
      }else if(e.deltaY > 1){
        if(e.deltaY > maxScrollBottom){
          $('#message').text('Extra Scroll Bottom');
          console.log('Extra Scroll Bottom');
        }

        // This can be removed if you dont need to detect the first scroll bottom.
        if(!scrollBottom){
          scrollBottom = true;
          $('#message').text('Scroll Bottom');
          console.log('Scroll Bottom');
        }
      }
    }else{
      // Clean Scroll positions.
      scrollTop     = false;
      scrollBottom  = false;
      $('#message').text('Not at the top/bottom edge');
      console.log('Not at the top/bottom edge');
    }

  }, {passive: true});


}); // End Document.ready

And here is the working example using CodePen.

http://codepen.io/xWaZzo/pen/NRorKj



来源:https://stackoverflow.com/questions/40134744/detect-intentional-top-and-bottom-extra-scroll

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!