防止自定义jQuery图像轮播中的事件重叠

更新:我在下面发布的解决方案不够好,因为它使除活动的子弹之外的所有子弹都不响应点击 ,而不是排队 ,因此还有改进的余地。


我正在使用jQuery和CSS处理自定义图像轮播。 我的目标是让它非常轻巧但具有(仅)足够的function:“子弹”,自动前进,响应能力。

它工作正常,但我发现了一个我无法解决的错误:当我快速连续点击2个子弹 – 这意味着在第一个触发的转换完成之前点击第二个 – 转换以奇怪的方式重叠我不能描述但在下面可见:

var $elm = $('.slider'), $slidesContainer = $elm.find('.slider-container'), slides = $slidesContainer.children('a'), slidesCount = slides.length, slideHeight = $(slides[0]).find('img').outerHeight(false), animationspeed = 1500, animationInterval = 7000; // Set (initial) z-index for each slide var setZindex = function() { for (var i = 0; i  1) { /* Prepend the slider navigation to the slider if there are at least 2 slides */ $elm.prepend('
    '); // make a bullet for each slide for (var i = 0; i < slidesCount; i++) { var bullets = '
  • ' + i + '
  • '; if (i == 0) { // active bullet var bullets = '
  • ' + i + '
  • '; // active slide $(slides[0]).addClass('active'); } $('.slider-nav').append(bullets); } }; var slideUpDown = function() { // set top property for all the slides $(slides).not(displayImageBeforeClick).css('top', slideHeight); // then animate to the next slide $(slides[activeIdx]).animate({ 'top': 0 }, animationspeed); $(displayImageBeforeClick).animate({ 'top': "-100%" }, animationspeed); }; $('.slider-nav a').on('click', function(event) { displayImageBeforeClick = $(".slider-container .active"); activeIdx = $(this).text(); if ($(slides[activeIdx]).hasClass("active")) { return false; } $('.slider-nav a').closest('li').removeClass('activeSlide'); $(this).closest('li').addClass('activeSlide'); // Reset autoadvance if user clicks bullet if (event.originalEvent !== undefined) { clearInterval(autoAdvance); autoAdvance = setInterval(advanceFunc, animationInterval); } setActiveSlide(); slideUpDown(); });
     body * { box-sizing: border-box; } .container { max-width: 1200px; margin: 0 auto; } .slider { width: 100%; height: 300px; position: relative; overflow: hidden; } .slider .slider-nav { text-align: center; position: absolute; padding: 0; margin: 0; left: 10px; right: 10px; bottom: 2px; z-index: 30; } .slider .slider-nav li { display: inline-block; width: 20px; height: 3px; margin: 0 1px; text-indent: -9999px; overflow: hidden; background-color: rgba(255, 255, 255, .5); } .slider .slider-nav a { display: block; height: 3px; line-height: 3px; } .slider .slider-nav li.activeSlide { background: #fff; } .slider .slider-nav li.activeSlide a { display: none; } .slider .slider-container { width: 100%; text-align: center; } .slider .slider-container a { display: block; position: absolute; top: 0; left: 0; right: 0; } .slider .slider-container img { transform: translateX(-50%); margin-left: 50%; } 
       

    我怎么能防止这种现象,因为缺乏一个更好的术语,一个事件拥挤(重叠)?

    您可以使用jQuery延迟对象和Promise来链接动画。 这是允许您轻松完成的课程。

     var Queue = function() { var lastPromise = null; this.add = function(callable) { var methodDeferred = $.Deferred(); var queueDeferred = this.setup(); // execute next queue method queueDeferred.done(function() { // call actual method and wrap output in deferred callable().then(methodDeferred.resolve) }); lastPromise = methodDeferred.promise(); }; this.setup = function() { var queueDeferred = $.Deferred(); // when the previous method returns, resolve this one $.when(lastPromise).always(function() { queueDeferred.resolve(); }); return queueDeferred.promise(); } }; 

    小提琴是排队的动画。

    PS:我增加按钮的大小以便更容易点击

     var $elm = $('.slider'), $slidesContainer = $elm.find('.slider-container'), slides = $slidesContainer.children('a'), slidesCount = slides.length, slideHeight = $(slides[0]).find('img').outerHeight(false), animationspeed = 1500, animationInterval = 7000; // Set (initial) z-index for each slide var setZindex = function() { for (var i = 0; i < slidesCount; i++) { $(slides[i]).css('z-index', slidesCount - i); } }; setZindex(); var setActiveSlide = function() { $(slides).removeClass('active'); $(slides[activeIdx]).addClass('active'); }; var advanceFunc = function() { if ($('.slider-nav li.activeSlide').index() + 1 != $('.slider-nav li').length) { $('.slider-nav li.activeSlide').next().find('a').trigger('click'); } else { $('.slider-nav li:first').find('a').trigger('click'); } } var autoAdvance = setInterval(advanceFunc, animationInterval); //Set slide height $(slides).css('height', slideHeight); // Append bullets if (slidesCount > 1) { /* Prepend the slider navigation to the slider if there are at least 2 slides */ $elm.prepend('
      '); // make a bullet for each slide for (var i = 0; i < slidesCount; i++) { var bullets = '
    • ' + i + '
    • '; if (i == 0) { // active bullet var bullets = '
    • ' + i + '
    • '; // active slide $(slides[0]).addClass('active'); } $('.slider-nav').append(bullets); } }; var Queue = function() { var lastPromise = null; this.add = function(callable) { var methodDeferred = $.Deferred(); var queueDeferred = this.setup(); // execute next queue method queueDeferred.done(function() { // call actual method and wrap output in deferred callable().then(methodDeferred.resolve) }); lastPromise = methodDeferred.promise(); }; this.setup = function() { var queueDeferred = $.Deferred(); // when the previous method returns, resolve this one $.when(lastPromise).always(function() { queueDeferred.resolve(); }); return queueDeferred.promise(); } }; var queue = new Queue(); var slideUpDown = function(previousIdx, activeIdx) { queue.add(function() { return new Promise(function(resolve, reject) { // set top property for all the slides $(slides).not(slides[previousIdx]).css('top', slideHeight); // then animate to the next slide $(slides[activeIdx]).animate({ 'top': 0 }, animationspeed); $(slides[previousIdx]).animate({ 'top': "-100%" }, animationspeed, 'swing', resolve); }) }) }; var previousIdx = '0' // First slide $('.slider-nav a').on('click', function(event) { activeIdx = $(this).text(); // Disable clicling on an active item if ($(slides[activeIdx]).hasClass("active")) { return false; } $('.slider-nav a').closest('li').removeClass('activeSlide'); $(this).closest('li').addClass('activeSlide'); // Reset autoadvance if user clicks bullet if (event.originalEvent !== undefined) { clearInterval(autoAdvance); autoAdvance = setInterval(advanceFunc, animationInterval); } setActiveSlide(); slideUpDown(previousIdx, activeIdx); previousIdx = activeIdx });
       body * { box-sizing: border-box; } .container { max-width: 1200px; margin: 0 auto; } .slider { width: 100%; height: 300px; position: relative; overflow: hidden; } .slider .slider-nav { text-align: center; position: absolute; padding: 0; margin: 0; left: 10px; right: 10px; bottom: 2px; z-index: 30; } .slider .slider-nav li { display: inline-block; width: 20px; height: 6px; margin: 0 1px; text-indent: -9999px; overflow: hidden; background-color: rgba(255, 255, 255, .5); } .slider .slider-nav a { display: block; height: 6px; line-height: 3px; } .slider .slider-nav li.activeSlide { background: #fff; } .slider .slider-nav li.activeSlide a { display: none; } .slider .slider-container { width: 100%; text-align: center; } .slider .slider-container a { display: block; position: absolute; top: 0; left: 0; right: 0; } .slider .slider-container img { transform: translateX(-50%); margin-left: 50%; } 
         

      这是一个可能的修复,包括在启动另一个之前等待动画完成:

       var $elm = $('.slider'), $slidesContainer = $elm.find('.slider-container'), slides = $slidesContainer.children('a'), slidesCount = slides.length, slideHeight = $(slides[0]).find('img').outerHeight(false), animationspeed = 1500, animationInterval = 7000; // Set (initial) z-index for each slide var setZindex = function() { for (var i = 0; i < slidesCount; i++) { $(slides[i]).css('z-index', slidesCount - i); } }; setZindex(); var displayImageBeforeClick = null; var setActiveSlide = function() { $(slides).removeClass('active'); $(slides[activeIdx]).addClass('active'); }; var advanceFunc = function() { if ($('.slider-nav li.activeSlide').index() + 1 != $('.slider-nav li').length) { $('.slider-nav li.activeSlide').next().find('a').trigger('click'); } else { $('.slider-nav li:first').find('a').trigger('click'); } } var autoAdvance = setInterval(advanceFunc, animationInterval); //Set slide height $(slides).css('height', slideHeight); // Append bullets if (slidesCount > 1) { /* Prepend the slider navigation to the slider if there are at least 2 slides */ $elm.prepend('
        '); // make a bullet for each slide for (var i = 0; i < slidesCount; i++) { var bullets = '
      • ' + i + '
      • '; if (i == 0) { // active bullet var bullets = '
      • ' + i + '
      • '; // active slide $(slides[0]).addClass('active'); } $('.slider-nav').append(bullets); } }; var animationStart = false; var slideUpDown = function() { animationStart = true; // set top property for all the slides $(slides).not(displayImageBeforeClick).css('top', slideHeight); // then animate to the next slide $(slides[activeIdx]).animate({ 'top': 0 }, animationspeed, function() { animationStart = false; }); $(displayImageBeforeClick).animate({ 'top': "-100%" }, animationspeed, function() { animationStart = false; }); }; $('.slider-nav a').on('click', function(event) { if (animationStart) { return false; } displayImageBeforeClick = $(".slider-container .active"); activeIdx = $(this).text(); if ($(slides[activeIdx]).hasClass("active")) { return false; } $('.slider-nav a').closest('li').removeClass('activeSlide'); $(this).closest('li').addClass('activeSlide'); // Reset autoadvance if user clicks bullet if (event.originalEvent !== undefined) { clearInterval(autoAdvance); autoAdvance = setInterval(advanceFunc, animationInterval); } setActiveSlide(); slideUpDown(); });
         body * { box-sizing: border-box; } .container { max-width: 1200px; margin: 0 auto; } .slider { width: 100%; height: 300px; position: relative; overflow: hidden; } .slider .slider-nav { text-align: center; position: absolute; padding: 0; margin: 0; left: 10px; right: 10px; bottom: 2px; z-index: 30; } .slider .slider-nav li { display: inline-block; width: 20px; height: 3px; margin: 0 1px; text-indent: -9999px; overflow: hidden; background-color: rgba(255, 255, 255, .5); } .slider .slider-nav a { display: block; height: 3px; line-height: 3px; } .slider .slider-nav li.activeSlide { background: #fff; } .slider .slider-nav li.activeSlide a { display: none; } .slider .slider-container { width: 100%; text-align: center; } .slider .slider-container a { display: block; position: absolute; top: 0; left: 0; right: 0; } .slider .slider-container img { transform: translateX(-50%); margin-left: 50%; } 
           

        您可以使用队列:每次单击子弹时,它都会添加到队列并执行队列。 像这样的东西:

         { let transitionQueue = [], transitioning = false; function doTransition() { displayImageBeforeClick = $(".slider-container .active"); $('.slider-nav a').closest('li').removeClass('activeSlide'); transitionQueue.shift().closest('li').addClass('activeSlide'); // Reset autoadvance if user clicks bullet if (event.originalEvent !== undefined) { clearInterval(autoAdvance); autoAdvance = setInterval(advanceFunc, animationInterval); } setActiveSlide(); slideUpDown(); if (transitionQueue.length) setTimeout(doTransition, animationSpeed) else transitioning = false; } function callTransition() { if (!transitioning) { transitioning = true; doTransition(); } } $('.slider-nav a').click(function () { transitionQueue.push($(this)); callTransition(); }); } 

        我没有测试过这个,所以……

        虽然这不是严格回答你的问题,但解决实际问题的一种方法是以不同的方式处理滑块,包括移动滑块而不是幻灯片:

        1)使幻灯片容器绝对,并且它是容器相对的

         .slider-homepage { position: relative; } .slider .slider-container { position: absolute; } .slider .slider-nav { //position: absolute; Remove this } 

        2)不要在点击时定位滑块,而是将滑块容器移动到正确的位置

         var slideUpDown = function() { $('.slider-container').stop().animate( {top: activeIdx * slideHeight * -1}, {duration: animationspeed} ); }; 

        这样,任何图像都不会重叠。

        这是小提琴 ,代码当然可以重构,但我没有太多时间来研究它,如果你没有离开你原来的移动幻灯片而不是滑块。