egr103
egr103

Reputation: 3978

Scroll function doesn't produce a smooth transition

I have 1 fixed DIV #scroll-up that is 100% height of the browser window. I have another DIV #next-prev that sits underneath it. As soon as #next-prev comes into view I want the #scroll-up DIV to begin scrolling up. My code blow does this, however as soon as #next-prev is in view #scroll-up just jumps up for no reason. The effect should be seamless.

How do I prevent the jump?

I have setup a demo of the problem on jsFiddle here: http://jsfiddle.net/sya0jr6p/1/


JS

Uses the jQuery Visible plugin to detect when #next-prev is in view.

$(window).scroll(function() {

    if ($('#next-prev').visible(true)) {
        console.log ( '#next-prev visible' );

        // Initiate scroll up of '#scroll-up' when '#next-prev' is in the viewport
        var $tagline = $('#scroll-up');
        var windowScroll;

        // Function
        function slidingTitle() {

            //Get scroll position of window
            windowScroll = $(this).scrollTop();

            $tagline.css({
                'top' : -(windowScroll/3)+"px"
            });

        }
        // Initiate
        slidingTitle();

    } else {
        console.log ( '#next-prev not visible' );
         // The element is NOT visible, do something else

    }

});

UPDATE

After further testing the jump ONLY exists when #nav-wrap isn't already on the screen. If it is on the screen then the jump doesn't occur and works as expected.

Another thing I noticed; When you first load the page AND #nav-wrap isn't already on the screen. Scroll down the page as normal until you see #scroll-up jump up and if you continue scrolling down you'll see what I want to happen. #scroll-up scrolling normally. BUT if after the jump has taken place you scroll back up the page to the very top, you'll notice the position of #scroll-up isn't the same as when you first loaded the page. Could the calculation wrong? or could the order of the code be wrong? Just a few ideas...

UPDATE

This is a animated GIF of how I want the scroll to work, the animate answers that have been given aren't intuitive:

enter image description here

Upvotes: 5

Views: 3709

Answers (5)

Mikk3lRo
Mikk3lRo

Reputation: 3496

The reason you see the jump is that you simply divide the current window scroll position once the #next-prev comes into view by 3 and set that as a top value for #scroll-up... the window scroll when you reach #next-prev will be larger than 0, hence the scroll divided by three will also be larger than zero, and #scroll-up will immediately get a (large) negative top value.

I don't know where you got the division by three from - that would create a slower scrolling of the element (aka. parallax effect) - which means that even if there was no jump and #scroll-up started to scroll at the right point, it would still cover two thirds of #next-prev once #next-prev was fully in view... So I don't think you actually want the parallax effect...

What I think you want is: The top value of #scroll-up needs to be zero until the first pixel of #next-prev scrolls into view, and then grow gradually as #next-prev comes "more" into view, making sure it stays exactly above #next-prev... ie. the top of #scroll-up needs to be 0 minus the number of pixels #next-prev's top is from the bottom of the viewport - and of course always zero if #next-prev is still completely below the viewport.

Here's a way to achieve that...

(function($) {
  $(window).scroll(function() {
    //Get the footer position relative to the document
    var footer_offset = $("#footer").position().top;
    //Get the window height (viewport height)
    var window_height = $(window).height();
    //Get the current scroll position
    var window_scroll = $(window).scrollTop();
    //Calculate the bottom of the window relative to the document
    var window_bottom_offset = window_scroll + window_height;
    //Calculate "how much" of the footer is above the bottom of the window
    var footer_visible_pixels = window_bottom_offset - footer_offset;
    //Default #semistuck's position to top: 0px
    var semistuck_top = 0;
    //But if the footer is above the bottom of the screen...
    if (footer_visible_pixels > 0) {
      //...move #semistuck up by the same amount of pixels
      semistuck_top = -footer_visible_pixels;
    }
    //Actually move it
    $("#semistuck").css('top', semistuck_top + 'px');
    //Just to see each step of the calculation
    console.log(footer_offset, window_height, window_scroll, window_bottom_offset, footer_visible_pixels, semistuck_top);
  });
}(jQuery));
body,
html {
  margin: 0;
}
#semistuck,
#loooooong,
#footer {
  margin: 0;
  padding: 4em 1em;
  text-align: center;
  box-sizing: border-box;
}
#semistuck {
  position: fixed;
  width: 50%;
  height: 100%;
  top: 0;
  left: 0;
  background: #afa;
}
#loooooong {
  height: 2000px;
  margin-left: 50%;
  width: 50%;
  background: repeating-linear-gradient(45deg, #606dbc, #606dbc 100px, #465298 100px, #465298 200px);
}
#footer {
  background: #faa;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="semistuck">
  This is stuck until the<br>footer scrolls into view
</div>
<div id="loooooong"></div>
<div id="footer">
  When this gets into<br>view #semistuck will<br>get out of its way
</div>

Obviously the above code is way more verbose than needed, but I felt that very small increments in the calculation makes it clearer what happens....

Here's the same thing in a shorter form:

var footer_visible_pixels = $(window).scrollTop() + $(window).height() - $("#footer").position().top;
$("#semistuck").css('top', (footer_visible_pixels > 0 ? -footer_visible_pixels : 0) + 'px');

Hmm...

...come to think of it you may actually want the parallax effect, but just want the footer to be on top, so here's a slightly modified version which gives a somewhat cool effect:

(function($) {
  $(window).scroll(function() {
    //Get the footer position relative to the document
    var footer_offset = $("#footer").position().top;
    //Get the window height (viewport height)
    var window_height = $(window).height();
    //Get the current scroll position
    var window_scroll = $(window).scrollTop();
    //Calculate the bottom of the window relative to the document
    var window_bottom_offset = window_scroll + window_height;
    //Calculate "how much" of the footer is above the bottom of the window
    var footer_visible_pixels = window_bottom_offset - footer_offset;
    //Default #semistuck's position to top: 0px
    var semistuck_top = 0;
    //But if the footer is above the bottom of the screen...
    if (footer_visible_pixels > 0) {
      //...move #semistuck up by the same amount of pixels
      semistuck_top = -footer_visible_pixels/3;
    }
    //Actually move it
    $("#semistuck").css('top', semistuck_top + 'px');
    //Just to see each step of the calculation
    console.log(footer_offset, window_height, window_scroll, window_bottom_offset, footer_visible_pixels, semistuck_top);
  });
}(jQuery));
body,
html {
  margin: 0;
}
#semistuck,
#loooooong,
#footer {
  margin: 0;
  padding: 4em 1em;
  text-align: center;
  box-sizing: border-box;
}
#semistuck {
  position: fixed;
  width: 50%;
  height: 100%;
  top: 0;
  left: 0;
  background: #afa;
}
#loooooong {
  height: 2000px;
  margin-left: 50%;
  width: 50%;
  background: repeating-linear-gradient(45deg, #606dbc, #606dbc 100px, #465298 100px, #465298 200px);
}
#footer {
  background: #faa;
  position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="semistuck">
  This is stuck until the<br>footer scrolls into view
</div>
<div id="loooooong"></div>
<div id="footer">
  When this gets into<br>view #semistuck will<br>get out of its way
</div>

Only two things are changed compared to the original suggestion:

  1. I've added position: relative to the footer
  2. I divide the negative top of #semistuck by three

Note

None of this will work on iPhones, iPads, and probably most other touch devices. They simply don't fire any scroll events during scroll, but only after. So if support for mobile devices is a concern, you should detect those and provide a fallback - the alternative is a very jumpy effect that happens only after scrolling ends. To the best of my knowledge there is no workaround, and depending on the scenario the best way to handle it may be to:

  1. just degrade to something simpler
  2. use one of the animated solutions provided in the other answers - but only on devices that do not support continuous scroll events

Upvotes: 5

Sohrab Hejazi
Sohrab Hejazi

Reputation: 1511

I think I got it working the way you want it to work. Let me know if this isn't the case. I changed the #scroll-up in CSS and added an extra class called .scroll-up-fixed.

You can see a demo here: http://jsfiddle.net/poj3ce51/

#scroll-up {
    position: absolute;
    background:aqua;
    height:100%;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    width: 50%;
}

.scroll-up-fixed {
    position: fixed !important;
}

I also changed the jQuery portion to the following:

$(window).scroll(function() {
    if ($('#next-prev').visible(true)) {
        console.log ( '#next-prev visible' );

        var $tagline = $('#scroll-up');
        var windowScroll;

        $tagline.removeClass("scroll-up-fixed");

        // Functional parallaxing calculations
        function slidingTitle() {

            //Get scroll position of window
            windowScroll = $(this).scrollTop();
            windowHeight = $(window).height();
            $tagline.css({
                'top' : (windowHeight)+"px"
            });

        }
        slidingTitle();

    } else {
        console.log ( '#next-prev not visible' );
         // The element is NOT visible, do something else
    }
});

Upvotes: 0

chillvivek
chillvivek

Reputation: 310

Does this help? Currently you are changing the top value of CSS. Why don't you add a transition to the change in top? See the fiddle example.

$tagline.css({
                    'top' : -(windowScroll/3)+"px",
                    'webkit-transition-property': 'top',
                    'webkit-transition-duration': '.5s'
                });

http://jsfiddle.net/vq94s6fw/

Upvotes: 0

Himesh Aadeshara
Himesh Aadeshara

Reputation: 2121

Hi need feed back whether it's your expected result or not.? or what you need to improve

$(window).on('scroll',function() {
    var $scrollUp = $("#scroll-up"),
        $nextDiv = $("#next-prev"),
        $divScrolled = $(this).scrollTop()/3;
    if ($nextDiv.visible(true)){
        $scrollUp.stop().animate({
            'top': - $divScrolled
        },'slow');
    }else{        
        $scrollUp.stop().animate({
            'top': 0
        },'slow');
        console.log("Nothing will be in your");
    }    
});
html,body{ height:100%; }
#scroll-up {
    position: fixed;
	top: 0;
    background:aqua;
    height:100%;
	left: 0;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
    width: 50%;
}

.demo-purposes { padding-top:200px;}

#next-prev { 
    background:pink; 
    text-align:center; 
    width: 100%;
    top: 10px;
	overflow: hidden;
	position: relative; }
<div id="scroll-up">
    <div class="demo-purposes"> <!-- This is just here so you can see the problem -->
        This content block should begin to scroll up when #next-prev is in the viewport. This does happen, however it isn't very smooth and this message will jump up a certain amount of pixels.</div>
</div>
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<br/>&nbsp;
<div id="next-prev">
    #next-prev DIV
    <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing s</p>
</div>

here i also give my working jsfiddle so that you can give clear feedback.

My Working Demo

Upvotes: 0

Pasindu Jay
Pasindu Jay

Reputation: 4510

The scroll jump is a browser default behaviour of your current browser. (unless you override it)

If you check that with firefox or in EI9+ you will see a smooth scroll.

Chrome has a setting called smooth scroll under chrome flags. ( If you are using using chrome )

Search how to force smooth scroll on browsers.

This answer might help.

Upvotes: -1

Related Questions