jQuery .stop() on scroll is blocking animation - javascript

this is my simple scroll function.
When user scrolls a certain distance from the bottom of the page, a registration box appears via animation:
$(window).scroll(function(){
if($docHeight - $top < 500 && document.cookie.indexOf("flag") < 0) {
$('.register').stop().animate({
'bottom': -1,
});
return false;
}
});
My problem is that the .stop() function before .animate() triggers every time the user scrolls; and so during animation it is constantly stopping as the user scrolls causing a 'juddering' effect (of course, no juddering if the user doesn't scroll).
I have thought about setting flags during animation and not running the animation to avoid this, but I can't figure it out.
I believe the function .once() could be used well here?
Thanks

Related

JS: preventDefault for User Scrolling? Or force GSAP scrolling over user?

I'm working on a slideshow area. I want to make it so scrolling horizontally will trigger an animation which moves the scrollable area to the next "slide."
Everything is working, but only if I scroll just a tiny bit. If I scroll more, the GSAP scrolling animation fails silently.
There are two ways that would make sense for this to be solved which I can think of:
1 > The first would be cancelling the scroll behavior, something like this:
$viewing_area.scroll( function(event) {
if(animationIsInProgress) {
event.preventDefault();
}
}
But this way is likely to stop GSAP scrolling as well. There is no way to distinguish if the scrolling is due to GSAP or the user that I know of.
2 > The second way would be to have GSAP force it's scrolling over anything the user is doing:
TweenLite.to($viewing_area, time, {
scrollTo: { x: slide_stops[nextTarget] },
ease: Power4.easeInOut,
onComplete: function() {
console.log('scrolling completed');
animationIsInProgress = false;
}
//Some option for forcing the behavior over user scrolling
});
Are either of these things achievable, or is scrolling by it's nature, unstoppable?
It ends up, GSAP scroll to plugin has a setting for this:
http://greensock.com/docs/#/HTML5/GSAP/Plugins/ScrollToPlugin/
It is called autokill, and if it is set to false, user scrolling will not interrupt the tween.
TweenLite.to(myDiv, 2, {scrollTo:{y:400, autoKill:false}, ease:Power2.easeOut});

jQuery no smooth .animate() transitions when using .stop()

I've build a sticky navigation that disappears when the user scrolls down, and re-appears when he/she scrolls back up. This is so there's more content visible on smaller screens when, for example, reading an article.
The problem I had was animations queue build-up, but I was able to fix it using .stop()
However, this created another problem, which is no smooth animations. When you keep scrolling up, the .stop() gets executed, and then the scroll-up animation continues, creating a weird animation effect.
$(document).ready(function() {
$(window).load(function() {
var $menu = $('.mobile-menu');
var previousTop = $menu.offset().top;
$(document).scroll(function() {
// Determine scroll position
var top = $(this).scrollTop();
// Check if user is scrolling up
if (top < previousTop) {
// .stop() prevents animation queue build-up
$menu.animate({
top: 0
}, 200);
}
else {
// .stop() prevents animation queue build-up
$menu.animate({
top: -$menu.outerHeight()
}, 200);
}
previousTop = top;
});
});
});
Fiddle: http://jsfiddle.net/8wwtLda3/
I know I can fix this using CSS transitions, but the script has to be reusable, and editing the CSS files isn't always an options.
Any tips on how to get smooth jQuery animations, without a queue building up?
SOLUTION: Thanks to Fonzy for telling me about .queue() I was able to fix this.
http://jsfiddle.net/8wwtLda3/1/

Flickering with jQuery animate scrollTop

My problem
I am making a vertical website for a client who wishes to have the window "snap" to the nearest page when most of the element is visible in the viewport. So, if the page is 85% visible, it should scroll to be 100% visible.
My problem is that occasionally when scrolling all the way to the top or bottom of the viewport, the viewport will "stick" to the first or last element, preventing a few scroll events and causing a highly noticeable flicker.
A working fiddle is here: http://jsfiddle.net/RTzu8/1/
To reproduce the error, use the scrollbar to scroll to the bottom of the page. Then, scroll up with your mousewheel. You should see the flicker. Sometimes it takes a few refreshes or attempts, but the issue is highly reproducible.
I'm at a loss as to what could be causing this issue. See below for a run-down of my code and what I have tried to prevent it so far.
My code
To accomplish my snapping, I needed to detect whether an element was a certain percentage visible. So, I added a jQuery function, isNearScreen, below. I have thoroughly tested this function, and as far as I can tell it returns accurate results.
//Modification of http://upshots.org/javascript/jquery-test-if-element-is-in-viewport-visible-on-screen
//Returns "true" if element is percent visible within the viewport
$.fn.isNearScreen = function(percent){
var offset = 1 - percent;
var win = $(window);
var viewport = {
top : win.scrollTop()
};
viewport.bottom = viewport.top + win.height();
var bounds = this.offset();
bounds.bottom = bounds.top + this.outerHeight();
bounds.top = bounds.top;
//If the element is visible
if(!(viewport.bottom < bounds.top || viewport.top > bounds.bottom)){
//Get the percentage of the element that's visible
var percentage = (viewport.bottom - bounds.top) / this.height();
//If it's greater than percent, but less than 1 + (1 - percent), return true;
return (percentage > (1 - offset) && percentage < (1 + offset));
}
return false;
};
I then created a snap function, which makes use of Underscore.js's _.debounce function, to only fire on the trailing end of continuous scroll events. It fires after a 500ms timeout, and I am fairly (though not 100%) convinced that it is firing correctly. I have not been able to reproduce console logs that would indicate multiple concurrent firings.
//Snaps the screen to a page after scroll input has stopped arriving.
var snap = _.debounce(function(event){
//Check each page view
$.each($('.page-contents'), function(index, element){
//If the page view is 70% of the screen and we are allowed to snap, snap into view
if($(element).isNearScreen(0.7)){
$('html,body').animate({
scrollTop: $(element).offset().top
}, 300);
}
});
}, 500);
Finally, I bind to the window's scroll event
$(window).on('scroll', snap});
The (extremely simplified) HTML:
<div class="page">
<div class="page-contents"></div>
</div>
<div class="page">
<div class="page-contents"></div>
</div>
<div class="page">
<div class="page-contents"></div>
</div>
<div class="page">
<div class="page-contents"></div>
</div>
and CSS:
.page{
height: 750px;
width: 100%;
margin: 10px 0;
background: gray;
}
.page-contents{
height: 100%;
width: 100%;
}
What I've tried
I have tried the following, with no success:
Setting a boolean, 'preventSnap', on the window, checking its state, and only firing the animate portion of snap if it is set to false. After animation, set it to true, then set it to false after 500ms (which should in theory prevent double firings).
Calling .stop() on the element before running the snap animation.
Calling event.preventDefault() on the scroll event before running the animation.
Reducing and increasing my _.debounce delay. Interestingly, a lower _.debounce delay (200-300ms) seems to aggravate the problem and a higher _.debounce delay (1000ms) seems to fix it. This is not an acceptable solution, however, as it feels "long" waiting 1sec for the page to "snap".
Changing the heights of the elements
If there is any other information I can provide, please let me know. I'm at a loss!
I think this is a combination of events and how _.debounce works. I noticed in the fiddle (in Chrome) that the elements were 'jitterring' long after the snap finished. If you put a console log in the snap event handler you can see it's constantly being called after a snap even with no scroll inputs.
This must be the scroll animation itself setting off the snap, I tried to set a flag to prevent dual snapping and clearing the flag after the animation was finished -- however that didn't work I think because _.debounce is queuing the event to happen later (after the animation finishes and clears the flag).
So what does work is to add this as the start of the snap handler:
var nosnap = false;
var snap = _.debounce(function(event){
// Don't snap if already animating ...
if (nosnap) { nosnap = false; return; }
nosnap = true;
Fiddle
That prevents the animation directly firing the next snap event -- however that's going to cause issues if you scroll again during the animation.
So, that's a bit of a hack. Ideally you want to be able to tell what's causing the scroll event and react accordingly but there's no easy way to do that.
I absolutely think you need to stop the animation when handling a second scroll event as well.

While Scrolling: if scrollTop() equals to or reaches a value execute event

I'm trying to fire an event when scrolling down to a certain point/value and execute again when scroll back up and down again.
I tried:
$(window).scroll(function(){
var sTop = $(this).scrollTop();
if(sTop == 300){
//execute event - failed
}
});
The above does not fire anything with the both mousewheel and dragging the scrollbar. but when I changed the condition to greater than or equal to, the event is being executed but multiple times when scrolling further down.
if(sTop >= 300){
//execute event - success
}
I also tried changing the condition value and some times the event is being fired.
if(sTop == 333 /* or 431, 658, etc. */){
//execute event - sometimes success
}
Here is my test fiddle.
Could it be that scroll() function sometimes skips a value if you scroll fast?
Can someone explain the issue and help me with a workaround on how to trigger an event when the $(window).scrollTop() == 'value'. Thanks
You're facing this issue because there are no guarantees that scrolling will be done in increments of 1. For example, some scroll wheels increment in values of 30. This means that you'd get a scroll event on 30, 60, 90, etc.
The best way to test if scrolling has reached your limit is to see if it has gone past the point you're wanting, then resetting the scroll to the precise point you're wanting. So you'd do:
if(sTop >= 300){
$(window).scrollTop(300);
}
Here's an updated link to your JSFiddle with a working example: http://jsfiddle.net/bC3Cu/4/
If you take a look at unit of increment in scrollable areas / divs in javascript? it describes how to set it to where you can override the scroll increment. Using that method, you should be able to predict how many units the user has scrolled without preventing them from scrolling further down the page.

Temporarily disable mousewheel until animation over

I'm working on a site that has a splash page and I want it to work so that when you scroll down the splash page, div.splash, will scroll up out of the way and the site scrolls in. I have it working for the most part, there is a just a small annoyance. Depending on how fast you scroll the mouse, it will scroll a certain distance into the main site.
I'd like it to work so that as soon as it detects even one click down on the mousewheel it will do all the stuff below but any additional movement in the mousewheel will be disabled until the slideUp()/animate() function is completed. That way when they scroll, the animations will happen but the person will still start at the very top of the page.
$(window).bind('mousewheel', function(e, delta) {
if(delta == -1){
disable_scroll();
$('.alpha.wrapper').show();
$('.titles .beta').hide();
$('.splash').slideUp(800);
$('.titles').animate({
top: '495px'
},800,enable_scroll());
}
});
By moving the disable_scroll(); call to the document ready you'll be able to disable the user from scrolling as soon as the page loads.
Then, change the above to:
$(window).bind('mousewheel', function(e, delta) {
if(delta == -1){
$('.alpha.wrapper').show();
$('.titles .beta').hide();
$('.splash').slideUp(800);
$('.titles').animate({
top: '495px'
},800,function(){enable_scroll();});

Categories

Resources