开发者

clearTimeout during gallery animation

开发者 https://www.devze.com 2023-03-27 17:52 出处:网络
I created a gallery with auto-play. When the gallery STOP it\'s animation and we hover the gallery, all works as expected.

I created a gallery with auto-play.

When the gallery STOP it's animation and we hover the gallery, all works as expected.

But I am getting a strange issue:

While the gallery is animating --> hovering the gallery, the clearTimeout seem not to work correctly and on mouseleave the gallery behave strangely

or it's happening a strange conflict I cannot understand and resolve. I went back to an other gallery I made a couple of days before fith a similar functionality and I encountered the same issue.

I am missing something crucial here

THE GALLERY IN QUESTION: jsFiddle

var myTimeOut;

/////// ANIMATION /////////////   
    function animation(cb){         
            $('#slider').animate({left: '-=600' },800, cb);
    }      
    /////// AUTO SLIDE ////////////
    function auto() {
        myTimeOut = setTimeout(function() {
            animation(function(){
                auto();                   
            });   
        }, 2000);
    }
    auto();

and how I'm trying to pause it:

    ///// MOUSE actions //////////   
    $('#galcontainer').mouseenter( function () {
      clearTimeout(myTimeOut);
    });
    $('#galcontainer').mouseleave( function () {
     auto();
    });

EDIT

Adding a 'hover flag' (as suggested in the answers) works almo开发者_StackOverflowst great, but a small bug using this solution is visible when 'fast mouseenter/mouseleave DURING the animation.

A fix to that issue could be adding a $('#slider').stop() at mouseleave. Not a great solution.

What else can I do?


When the animation is in progress sometimes the mouseout event is not getting triggered so pause variable is not set to false. when next mouseoever event is triggered since pause is not false it will again set the timeout. I just made sure we clear any previous timeout before setting the new one.

Take a look at this working demo.

http://jsfiddle.net/8Lzxs/3/


The problem is that during the animation, there is no timeout to stop. The one timeout has already fired and other is not created until the animation completes. Because of this, if you hover when the animation is in progress, the animation continues. You can solve this with a flag to tell whether or not the animation should be stopped. If the flag is set, auto returns and does not set another timeout.

http://jsfiddle.net/PXVSW/6/


Without digging too deeply into your code, I fixed it by adding a couple of extra checks.

First, I made it so there's no worry about the timeOut identifier getting replaced. I use an array in case multiple events get stacked on top of each other, and clear all of the timeouts in that array.

Second, I added a stop() value and made it so the animation won't run if stopped is true.

Here's the fiddle with my changes: http://jsfiddle.net/6WhNw/


It doesn't work if your mouse enters while the animation is running. That's because the timer has already fired and the end of the animation is going to start a new one when it completes. The least rewrite I can think of to solve this would be to set a global flag when you're hovered that the auto function checks so it won't start a new animation if that global flag is set.

You can see the hoverFlag solution here: http://jsfiddle.net/jfriend00/EaKnj/.

An interesting case to test is to make your mouse enter when the animation is running.


I found that scrolling (which is, well, a sort of animation too) on iOS 6 seems to cause the same problem: timeouts are not cleared reliably as long as a view scrolls.

I considered this an iOS Bug and - in my case - found a solution not using set and clearTimeout.

The same issue couldn't be reproduced in (several) Android Stock Browsers and Chrome.


You need to be using setInterval instead of setTimeout. Since the animation was calling the callback, it would call the animation over and over.

http://jsfiddle.net/PXVSW/7/

var galW =  $('#gallery').width() ,
    imgN =  $('#slider img').length ,
    direction,      // direction ( -=  or  0- ) // set initially to 'null','undefined'
    counter = 1,    // counter
    myTimeOut;       // global setTimeout

$('#slider').width(galW * imgN);

/////// ANIMATION /////////////   
function animation(){                 // cb : makes animation accept a callback
        $('#slider').animate({left: direction+''+galW},800);
}  

/////// AUTO SLIDE ////////////
function auto() {
    myTimeOut = setInterval(function() {
        if ( counter != imgN){           // if counter != '4' (N of Images)
            counter++                    // add '1' to counter
            direction = '-=';            // set direction to go 'left' 
        }
        else{
            counter = 1;
            direction = '0-';            // trick - to bring slider to '0px' left (start position)
        }
        animation();   
    }, 2000);
}
auto();

///// MOUSE STATE //////////   
$('#galcontainer').mouseenter( function () {
  clearInterval(myTimeOut);
});
$('#galcontainer').mouseleave( function () {
 auto();
});


The issue: on mouseenter during the animation, the jQuery .animate() callback cb is already in queue (even if the clearTimeout was called, another auto() will be called which will trigger subsequent runs, etc.).

Using setInterval and leveraging all those callbacks is the key:

jsFiddle demo

$(function(){                 // DOM ready shorthand
    var $sli =  $("#slider"), // Cache elements you plan to reuse
        galW =  $('#gallery').width(),
        imgN =  $sli.find('img').length,
        counter = 0,          // set it to index 0!
        itv;                  // setInterval (scope var)
    
    $sli.width(galW * imgN);

    function anim() {
      ++counter; counter%=imgN;                       // Incr.counter; and wrap-around
      $sli.stop().animate({left: -galW*counter},900); // width*counter = px position
    }
    function autoSlide() {
        itv = setInterval(anim, 3000);
    }
    autoSlide();
    
    ///// MOUSE STATE //////////   
    $sli.hover(function(){ clearInterval(itv); }, autoSlide);
});
#galcontainer{
    position:relative;
    margin:20px auto;
    width:600px;
}
#gallery{
    position:relative;
    overflow:hidden;
    width:600px;
    height:240px;
}
#slider{
    position:absolute;
    top:0px;
    left:0px;
}
#slider img{
    float:left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="galcontainer">    
    <div id="gallery">    
        <div id="slider">
            <img src="http://dummyimage.com/600x240/479/fff"/>
            <img src="http://dummyimage.com/600x240/636/fff"/>
            <img src="http://dummyimage.com/600x240/587/fff"/>
            <img src="http://dummyimage.com/600x240/447/fff"/>
        </div>     
    </div>   
</div>

(Aside notes regarding the original (yet modified) code)

The direction variable with "+=" and "0-" is unneeded since there's only one direction, so use a counter = 0 indexed, and wrap-around/reset using the Reminder % operator. Also,

        animation(function(){
            auto();                   
        }); 

can be written like:

       animation(auto);

since auto is already a function.

///// MOUSE STATE //////////   
$('#galcontainer').mouseenter( function () {
  clearInterval(myTimeOut);
});
$('#galcontainer').mouseleave( function () {
 auto();
});

can be used with .hover(fn1, fn2) method (like in the above example)

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号