代码之家  ›  专栏  ›  技术社区  ›  Razvan Zamfir

防止自定义jQuery图像旋转木马中的事件重叠

  •  3
  • Razvan Zamfir  · 技术社区  · 7 年前

    更新: 这个 solution 对点击没有反应 ,而不是 排队等候 ,所以还有改进的余地。


    轻便的

    它工作得很好,但是我发现了一个我无法修复的错误:当我连续快速点击两个子弹时-这意味着 在第一个触发的转换完成之前单击第二个 -转换以一种奇怪的方式重叠,我无法描述,但可以在下面看到:

    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('<ul class="slider-nav"></ul>');
      
      // make a bullet for each slide
      for (var i = 0; i < slidesCount; i++) {
        var bullets = '<li><a href="#">' + i + '</a></li>';
        if (i == 0) {
          // active bullet
          var bullets = '<li class="activeSlide"><a href="#">' + i + '</a></li>';
          // 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%;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    
    <div class="container">
      <div class="slider slider-homepage">
        <div class="slider-container">
          <a href="#">
            <img src="https://picsum.photos/1200/300/?gravity=east" alt="">
          </a>
          <a href="#">
            <img src="https://picsum.photos/1200/300/?gravity=south" alt="">
          </a>
          <a href="#">
            <img src="https://picsum.photos/1200/300/?gravity=west" alt="">
          </a>
        </div>
      </div>
    </div>

    如果没有更好的术语,我怎么能避免这种现象,我会称之为事件拥挤(重叠)?

    4 回复  |  直到 7 年前
        1
  •  2
  •   Razvan Zamfir    7 年前

    可以使用 jQuery deferred object 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();
        }
    };
    

    小提琴是与动画排队。

    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('<ul class="slider-nav"></ul>');
      
      // make a bullet for each slide
      for (var i = 0; i < slidesCount; i++) {
        var bullets = '<li><a href="#">' + i + '</a></li>';
        if (i == 0) {
          // active bullet
          var bullets = '<li class="activeSlide"><a href="#">' + i + '</a></li>';
          // 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%;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    
    <div class="container">
      <div class="slider slider-homepage">
        <div class="slider-container">
          <a href="#">
            <img src="https://picsum.photos/1200/300/?gravity=east" alt="">
          </a>
          <a href="#">
            <img src="https://picsum.photos/1200/300/?gravity=south" alt="">
          </a>
          <a href="#">
            <img src="https://picsum.photos/1200/300/?gravity=west" alt="">
          </a>
        </div>
      </div>
    </div>
        2
  •  2
  •   Razvan Zamfir    7 年前

    下面是一个可能的修复方法,包括在开始另一个动画之前等待动画完成:

    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('<ul class="slider-nav"></ul>');
      
      // make a bullet for each slide
      for (var i = 0; i < slidesCount; i++) {
        var bullets = '<li><a href="#">' + i + '</a></li>';
        if (i == 0) {
          // active bullet
          var bullets = '<li class="activeSlide"><a href="#">' + i + '</a></li>';
          // 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%;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    
    <div class="container">
      <div class="slider slider-homepage">
        <div class="slider-container">
          <a href="#">
            <img src="https://picsum.photos/1200/300/?gravity=east" alt="">
          </a>
          <a href="#">
            <img src="https://picsum.photos/1200/300/?gravity=south" alt="">
          </a>
          <a href="#">
            <img src="https://picsum.photos/1200/300/?gravity=west" alt="">
          </a>
        </div>
      </div>
    </div>
        3
  •  1
  •   Lucas Noetzold    7 年前

    您可以使用队列:每次单击项目符号时,它都会添加到队列中并执行队列。像这样的:

    {
        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();
        });
    }
    

        4
  •  0
  •   Logar    7 年前

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

    .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}
        );
    };
    

    这样就不会有图像重叠了。

    这是 fiddle