Disable scroll while waiting to for scroll selection animation to complete - javascript

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?

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.

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:
Animation started.
Flag animating = true.
Block scrolling.
Animation ended.
Flag animating = false.
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>

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/

Related

How can I return false when scrolling down?

I found a lot of examples of console logging scroll direction. But even though I tried modifying almost all of them to return a value instead, I couldn't make them work.
My goal is to check the scrolling direction when entering a specific viewport, and return false if the scrolling direction is down. Then I want to use the false value in a exitviewport function to only then send ONE ReactGA event of scrolling. My attempts have either resulted in 'cannot read property of undefined' error or to a lot of events which I do not want. I think my main problem is combining a return with the fact that there is a reassignment that needs to be reached by the code.
My code so far is:
//this code I found and it seems to work
onEnterViewPort () {
window.onscroll = function (e) {
console.log(this.oldScroll > this.scrollY);
this.oldScroll = this.scrollY;
};
}
//used console logs to check.
onExitViewPort () {
console.log('I have left the viewport.')
// ReactGA.event({
// category: 'Scroll',
// action: 'Scrolled down',
// });
}
which I use in
<ScrollTrigger onEnter={this.onEnterViewPort} onExit={this.onExitViewPort} triggerOnLoad={false}>
<div >
<thingshere>
</div>
</ScrollTrigger>
Please help a noob out!
Example with jQuery.
This will start to display the scrolling direction after u entered the Viewpoint the first time, so you would need to create another funtion for the onExitViewpoint event...
$.fn.onEnterViewport = function (callback) {
var $window = $(window);
return this.each(function () {
var that = this;
var oldHeight = 0;
$(document).on('scroll', function () {
var docHeight = $window.height();
var myTop = $(this).scrollTop();
var myOffset = $(that).offset();
var top = myOffset.top - myTop;
if(top < docHeight){
callback(oldHeight, top);
}
oldHeight = top;
});
});
};
$('.viewpoint-in').onEnterViewport(function(oldHeight, newHeight) {
if (oldHeight > newHeight) {
console.log('Scroll Down')
} else {
console.log('Scroll Top')
}
});
*{
padding: 0;
margin: 0;
}
.viewpoint-out {
background: red;
height: 1000px;
}
.viewpoint-in {
background: white;
height: 600px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<div class="viewpoint-out"></div>
<div class="viewpoint-in"></div>
<div class="viewpoint-out"></div>
</section>

Detect if user begin to scroll jQuery

Here's my code :
if ($('document').scrollTop() < 0) {
//Automatic Scroll
setTimeout(function () {
$('html, body').animate({
scrollTop: $('.main-header').offset().top - 0
}, 1800, 'easeInOutQuad');
},8000);
}
If a user does not scroll, the page scrolls automatically to a certain div.
But I don't know how to trigger when a user do not scroll.
Thanks for your help !
To detect if a user hasn't scrolled, what I would do is set up a hasScrolled variable.
var hasScrolled = false;
Then change that variable to 'true' if the user scrolls:
document.addEventListener("scroll", function(){ hasScrolled = true; });
Then do your setTimeout to see if, in 8 seconds, the user has scrolled, and if not, do your thing:
setTimeout(triggerScroll,8000);
And in your triggerScroll function, the first line could be if (hasScrolled) return so that it doesn't run if they've scrolled
https://jsfiddle.net/eergdw3v/
try this
document.body.scrollTop === 0
You need to create a timer at the scroll event using setTimeout
function setUnscrollEvent( scrollTimeout, callback )
{
$( window).scroll(function() {
if ( window.scrollTimer )
{
clearTimeout( window.scrollTimer ); //if the scroll has already started then clear the timeout
}
window.scrollTimer = setTimeout( function(){
callback(); //invoke the callback in case scroll has been idle for srollTimeout ms
}, scrollTimeout );
});
}
You need to invoke it this way
setUnscrollEvent( 100, function(){
console.log( "no scrolling for 100ms" );
});
DEMO
function setUnscrollEvent( scrollTimeout, callback )
{
$( window).scroll(function() {
if ( window.scrollTimer )
{
clearTimeout( window.scrollTimer ); //if the scroll has already started then clear the timeout
}
window.scrollTimer = setTimeout( function(){
callback(); //invoke the callback in case scroll has been idle for srollTimeout ms
}, scrollTimeout );
});
}
setUnscrollEvent( 100, function(){
console.log( "no scrolling for 100ms" );
});
.container
{
width: 1000px;
height: 1000px;
background-color: #ccc;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
</div>
let scrollHandler = function(e) {
//do your stuff
}
$(window).on("scroll",scrollHandler);
That will detect scrolling for you, to answer the question in your title. I don't follow what you're trying to say under your code though
Export your function, so that you can call it when you want :
if ($('document').scrollTop() < 0) {
//Automatic Scroll
setTimeout(triggerScroll,8000);
}
function triggerScroll() {
$('html, body').animate({
scrollTop: $('.main-header').offset().top - 0
}, 1800, 'easeInOutQuad');
}
// Manual trigger :
triggerScroll();

Trigger Greensock animation if body hasClass

I have a class of .header-is-active that is applied to the <body> tag when a user scrolls.
I would like an animation to be triggered when the class is added and then the animation to run in reverse when the class is removed.
Everything happens as expected except for the animation. I'm using jQuery and Greensock to make everything happen.
Here's what I currently have:
$(function() {
var body = $('body');
var trigger = $('.trigger');
var tween = TweenMax.to(trigger, 0.5, {css:{height: "100vh"},});
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 50) {
body.addClass('header-is-active');
tween();
} else {
body.removeClass('header-is-active');
}
});
});
The issue I have is the tweened value height: 100vh is being applied regardless with .header-is-active or not. Is there something missing?
At the end of your $(window).scroll() function, give this a shot:
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 50) {
body.addClass('header-is-active');
tween();
} else {
body.removeClass('header-is-active');
}
// jQuery way
if(body.hasClass('header-is-active')) {
// Run animation
} else {
// Run reverse animation
}
// Non-jQuery way (just for reference)
var body = document.body;
if(body.classList.contains('header-is-active') {
// Run animation
} else {
// Run reverse animation
}
});
If it doesn't work, since it's running async, you can add a setTimeout() to the above so it's called after your function. It should look something like this
setTimeout(function() {
if(body.hasClass('header-is-active')) {
// run animation
} else {
// run reverse
}
}, 0);

Vanilla Javascript, mouseout delay with cancelation without jQuery

i am currently having a problem. I am using css to hide and display elements depending on the mouse function. One of my elements (a navigation arrow) depends on some other things. I now need a cancleable timer function which counts for lets say 2 seconds on mouseleave and then changes the class attribute. But it should have a timer which cancels on mouseover immediately. I dont want it to disappear too early.
Below my code with which i tried so far. I have no idea how to access the current timings of that setIntervall stuff. I alreasy experimented with Date.now(). But now i hope some of the geeks is able to help me.
Thanks in advance.
function hideElementOnMouseOut(el)
{
el.addEventListener("mouseleave", function( event )
{
mySlideAction = setInterval( function()
{
}, 1000 );
}
}
You can initialize interval on mouseleave function and clear this interval on mouseover function, which would prevent executing it's function.
Check the snippet below.
function hideElementOnMouseOut(el)
{
var interval;
el.addEventListener("mouseleave", function(event)
{
el.innerHTML = 'mouse out';
interval = setInterval(function()
{
el.innerHTML = 'time out';
el.className = 'out';
}, 1000);
});
el.addEventListener("mouseover", function(event)
{
el.innerHTML = 'mouse in';
el.className = '';
if(interval) {
clearInterval(interval);
}
});
}
hideElementOnMouseOut(document.getElementById("element"));
#element {
display: block;
width: 200px;
height: 200px;
background: red;
}
#element.out {
background: blue;
}
<div id="element"></div>

jQuery: I need an antispam on my function

I have a function which reveals a div in my nav when clicked, and you can toggle it, so it hides when you press it again.
The problem is, that you can spamclick it so the animation keeps going and breaks the div.
What I need is some kind of antispam click, I want it to wait until the first animation is done.
Here is the code:
$("#navDown").click(function(){
if($("#navExpand").height()===0){
$('#navArrow').animateRotate(0, -180);
$("#navExpand").animate({
height: 150
})
$(".navExCon").delay(300).fadeToggle(150);
}
else if($("#navExpand").height()===150)
{
$(".navExCon").fadeToggle(150);
$('#navArrow').animateRotate(180, 0);
$("#navExpand").delay(300).animate({
height: 0
});
};
});
UPDATE /solution
This is what I did:
note: it would be better with the use of color on this site, to see the changes.
$("#navDown").click("slow",function(){ // added time "slow": 600 u i think.
if($("#navExpand").height()===0){
$('#navArrow').animateRotate(0, -180);
$("#navExpand").animate({
height: 150
},200) // added some time
$(".navExCon").fadeToggle(100);
}
else if($("#navExpand").height()===150)
{
$(".navExCon").fadeToggle(250);
$('#navArrow').animateRotate(180, 0);
$("#navExpand").animate({
height: 0
},200); // added some time
};
});
You can have a callback function when animation is finished:
var animating = false;
$("#navDown").click(function(){
if (animating) {
return; // Don't start new animation
}
animating = true;
if($("#navExpand").height()===0){
$('#navArrow').animateRotate(0, -180);
$("#navExpand").animate({
height: 150
})
$(".navExCon").delay(300).fadeToggle(150, function() {
// Will be called after animation is finished
animating = false;
});
}
else if($("#navExpand").height()===150)
{
$(".navExCon").fadeToggle(150);
$('#navArrow').animateRotate(180, 0);
$("#navExpand").delay(300).animate({
height: 0
}, function() {
// Will be called after animation is finished
animating = false;
});
}
});

Categories

Resources