Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect Intentional Top and Bottom extra scroll

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: enter image description here

like image 201
xWaZzo Avatar asked Oct 19 '16 14:10

xWaZzo


1 Answers

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.
;)

like image 196
Louys Patrice Bessette Avatar answered Nov 09 '22 07:11

Louys Patrice Bessette