javascript - 如何防止 setInterval() 倍增?

标签 javascript

我正在修改A Better Simple Slideshow具有暂停能力。我已经设法将 autoCycle() 中的暂停和取消暂停分离到它们自己的方法中(pauseCycle 和 unpauseCycle),因此我可以将它们用于暂停控制以及 autoCycle()。我创建了 addPause() 方法来生成暂停控件。 addPause() 方法成功地暂停了幻灯片放映,但在取消暂停幻灯片放映时使用 setInterval() 或传递给它的间隔变量做了一些奇怪的事情。它似乎导致另外一个 setInterval() 同时运行。代码如下:

var aFSlides = function(el, options) {
  var $slideshows = document.querySelectorAll(el), // a collection of all of the slideshows
    $slideshow = {},
    Slideshow = {
      init: function(el, options) {
        this.counter = 0; // to keep track of current slide
        this.interval = 0; // to control autoCycle
        this.paused = 0; // to keep track of whether paused or not
        this.el = el; // current slideshow container
        this.$items = el.querySelectorAll('figure'); // a collection of all of the slides, caching for performance
        this.numItems = this.$items.length; // total number of slides
        options = options || {}; // if options object not passed in, then set to empty object 
        // options.auto = options.auto || false; // if options.auto object is not passed in, then set to false
        this.opts = {
          auto: (typeof options.auto === "undefined") ? false : options.auto,
          speed: (typeof options.speed === "undefined") ? 6000 : options.speed,
          pause: (typeof options.pause === "undefined") ? false : options.pause,
          pauseOnHover: (typeof options.pauseOnHover === "undefined") ? true : options.pauseOnHover,
        };
        this.$items[0].classList.add('current'); // add .current class to first figure
        if (this.opts.auto) {
          this.autoCycle(this.el, this.opts.speed, this.opts.pauseOnHover);
        }
        if (this.opts.pause) {
          this.addPause(this.el);
        }
      },
      showCurrent: function(i) {
        // increment or decrement this.counter depending on whether i === 1 or i === -1
        if (i > 0) {
          this.counter = (this.counter + 1 === this.numItems) ? 0 : this.counter + 1;
        } else {
          this.counter = (this.counter - 1 < 0) ? this.numItems - 1 : this.counter - 1;
        }
        // remove .show from whichever element currently has it 
        // http://stackoverflow.com/a/16053538/2006057
        [].forEach.call(this.$items, function(el) {
          el.classList.remove('current');
        });
        // add .show to the one item that's supposed to have it
        this.$items[this.counter].classList.add('current');
      },
      pauseCycle: function(el, speed) {
        var that = this;
        interval = clearInterval(interval);
        el.classList.add('paused');
      },
      unpauseCycle: function(el, speed) {
        var that = this;
        interval = window.setInterval(function() {
          that.showCurrent(1); // increment & show
        }, speed);
        el.classList.remove('paused');
      },
      addPause: function(el, speed) {
        var spanPause = document.createElement("span"),
          docFrag2 = document.createDocumentFragment(),
          that = this,
          thatSpeed = speed;
        spanPause.classList.add('pause');
        spanPause.innerHTML = 'Pause';
        docFrag2.appendChild(spanPause);
        el.appendChild(docFrag2);
        togglePause = function(el, speed) {
          if (that.paused == 1) {
            var speed = that.opts.speed;
            that.unpauseCycle(el, speed);
            that.paused = 0;
            return that.paused;
          } else if (that.paused == 0) {
            var speed = that.opts.speed;
            interval = clearInterval(interval);
            that.pauseCycle(el, speed);
            that.paused = 1;
            return that.paused;
          }
        }
        el.querySelector('.pause').addEventListener('click', function() {
          togglePause(el, speed);
        }, false);
      },
      autoCycle: function(el, speed, pauseOnHover) {
        var that = this;
        if (that.paused == 0) {
          that.unpauseCycle(el, speed);
        }
        if (pauseOnHover) {
          el.addEventListener('mouseover', function() {
            if (that.paused == 0) {
              that.pauseCycle(el, speed);
            }
          }, false);
          el.addEventListener('mouseout', function() {
            if (that.paused == 0) {
              that.unpauseCycle(el, speed);
            }
          }, false);
        } // end pauseonhover
      } // end autoCycle
    }; // end Slideshow object .....
  // make instances of Slideshow as needed
  [].forEach.call($slideshows, function(el) {
    $slideshow = Object.create(Slideshow);
    $slideshow.init(el, options);
  });
};

/* Init for this example snippet */

var aFS56c641d29d032 = {
  auto: true,
  speed: 2000,
  pause: true,
};
aFSlides('.aFS56c641d29d032', aFS56c641d29d032);
body {
  font: 400 10px/1.3 Menlo, Courier, sans-serif;
}
figure {
  display: none;
}
figure.current {
  display: block;
}
figure pre {
  font: 700 24px/1.3 Menlo, Courier, sans-serif;
  white-space: pre;
}
span {
  background: #f66;
  color: #fff;
  font: 700 16px/1.3 Menlo, Courier, sans-serif;
  padding: 10px 20px;
  display: inline-block;
}
<div id="smile-gallery" class="aFS56c641d29d032 ">
  <div class="slides">
    <figure class="slide" id="bna-1">
      <div class="slide-content">
        <pre>
 ________________________
|                        |
|                        |
|         0    0         |
|                        |
|       \________/       |
|                        |
|________________________|
</pre>
      </div>
    </figure>
    <figure class="slide" id="bna-2">
      <div class="slide-content">
        <pre>
 ________________________
|                        |
|                        |
|    o              O    |
|                        |
|                        |
|         ______/        |
|________________________|
</pre>
      </div>
    </figure>
    <figure class="slide" id="bna-3">
      <div class="slide-content">
        <pre>
 ________________________
|                        |
|                        |
|       ^       ^        |
|                        |
|                        |
|        (EEEEE)         |
|________________________|
</pre>
      </div>
    </figure>
    <figure class="slide" id="bna-4">
      <div class="slide-content">
        <pre>
 ________________________
|                        |
|                        |
|       |       |        |
|     ____________       |
|     \          /       |
|      \________/        |
|________________________|
</pre>
      </div>
    </figure>
  </div>
  <!-- /.slides  -->
</div>
<!-- /#smile-gallery -->
<p>
  Taken from <a href="https://github.com/leemark/better-simple-slideshow" target="_blank">A Better Simple Slideshow</a>.
</p>

当您运行此脚本时,如果您使用“暂停”控件,则会发生一些奇怪的事情。首先,它会暂停并禁用pauseOnHover 条件,就像它应该做的那样。然后您再次单击它,它会取消暂停并开始一次前进两张幻灯片。 PauseOnHover 再次起作用,但仅在悬停时暂停一张幻灯片的前进,因此幻灯片仍然一次前进一张幻灯片。再次单击“暂停”,它会停止前进两次,但会继续一次前进一张幻灯片(现在应该暂停)。再次单击 ,它开始一次前进三帧(如果您再次将鼠标悬停在其上,则前进两帧),依此类推。每次都会不断添加一些东西,但我不知道它是什么。请帮忙。

谢谢!

2016 年 2 月 22 日更新

Here's a JSFiddle我一直在为这个项目工作。让pause和pauseOnHover同时工作是一场噩梦。

最佳答案

区间变量需要绑定(bind)到正确的范围(this)。因此,无论您在何处引用变量“interval”,都需要在其前面加上“this”前缀。

在代码中搜索“CHANGED”以查看我在何处进行了更改。

var aFSlides = function(el, options) {
  var $slideshows = document.querySelectorAll(el), // a collection of all of the slideshows
    $slideshow = {},
    Slideshow = {
      init: function(el, options) {
        this.counter = 0; // to keep track of current slide
        this.interval = 0; // to control autoCycle
        this.paused = 0; // to keep track of whether paused or not
        this.el = el; // current slideshow container
        this.$items = el.querySelectorAll('figure'); // a collection of all of the slides, caching for performance
        this.numItems = this.$items.length; // total number of slides
        options = options || {}; // if options object not passed in, then set to empty object 
        // options.auto = options.auto || false; // if options.auto object is not passed in, then set to false
        this.opts = {
          auto: (typeof options.auto === "undefined") ? false : options.auto,
          speed: 300, // CHANGED: faster for development
          //speed: (typeof options.speed === "undefined") ? 6000 : options.speed,
          pause: (typeof options.pause === "undefined") ? false : options.pause,
          pauseOnHover: (typeof options.pauseOnHover === "undefined") ? true : options.pauseOnHover,
        };
        this.$items[0].classList.add('current'); // add .current class to first figure
        if (this.opts.auto) {
          this.autoCycle(this.el, this.opts.speed, this.opts.pauseOnHover);
        }
        if (this.opts.pause) {
          this.addPause(this.el);
        }
      },
      showCurrent: function(i) {
        // increment or decrement this.counter depending on whether i === 1 or i === -1
        if (i > 0) {
          this.counter = (this.counter + 1 === this.numItems) ? 0 : this.counter + 1;
        } else {
          this.counter = (this.counter - 1 < 0) ? this.numItems - 1 : this.counter - 1;
        }
        // remove .show from whichever element currently has it 
        // http://stackoverflow.com/a/16053538/2006057
        [].forEach.call(this.$items, function(el) {
          el.classList.remove('current');
        });
        // add .show to the one item that's supposed to have it
        this.$items[this.counter].classList.add('current');
      },
      pauseCycle: function(el, speed) {
        var that = this;
        clearInterval(this.interval); // CHANGED: clearInterval doesn't return anything usefull
        el.classList.add('paused');
      },
      unpauseCycle: function(el, speed) {
        var that = this;
        // CHANGED x2: 
		window.clearInterval(this.interval);
        this.interval = window.setInterval(function() {
          that.showCurrent(1); // increment & show
        }, speed);
        el.classList.remove('paused');
      },
      addPause: function(el, speed) {
        var spanPause = document.createElement("span"),
          docFrag2 = document.createDocumentFragment(),
          that = this,
          thatSpeed = speed;
        spanPause.classList.add('pause');
        spanPause.innerHTML = 'Pause';
        docFrag2.appendChild(spanPause);
        el.appendChild(docFrag2);
        togglePause = function(el, speed) {
     
          if (that.paused == 1) {
            var speed = that.opts.speed;
            that.unpauseCycle(el, speed);
            that.paused = 0;
            return that.paused;
          } else if (that.paused == 0) {
            var speed = that.opts.speed;
            // CHANGED
            clearInterval(that.interval);
            //interval = clearInterval(that.interval);
            that.pauseCycle(el, speed);
            that.paused = 1;
            return that.paused;
          }
        }
        el.querySelector('.pause').addEventListener('click', function() {
          togglePause(el, speed);
        }, false);
      },
      autoCycle: function(el, speed, pauseOnHover) {
        var that = this;
        if (that.paused == 0) {
          that.unpauseCycle(el, speed);
        }
        if (pauseOnHover) {
          el.addEventListener('mouseover', function() {
            if (that.paused == 0) {
              that.pauseCycle(el, speed);
            }
          }, false);
          el.addEventListener('mouseout', function() {
            if (that.paused == 0) {
              that.unpauseCycle(el, speed);
            }
          }, false);
        } // end pauseonhover
      } // end autoCycle
    }; // end Slideshow object .....
  // make instances of Slideshow as needed
  [].forEach.call($slideshows, function(el) {
    $slideshow = Object.create(Slideshow);
    $slideshow.init(el, options);
  });
};

/* Init for this example snippet */

var aFS56c641d29d032 = {
  auto: true,
  speed: 2000,
  pause: true,
};
aFSlides('.aFS56c641d29d032', aFS56c641d29d032);
body {
  font: 400 10px/1.3 Menlo, Courier, sans-serif;
}
figure {
  display: none;
}
figure.current {
  display: block;
}
figure pre {
  font: 700 24px/1.3 Menlo, Courier, sans-serif;
  white-space: pre;
}
span {
  background: #f66;
  color: #fff;
  font: 700 16px/1.3 Menlo, Courier, sans-serif;
  padding: 10px 20px;
  display: inline-block;
}
<div id="smile-gallery" class="aFS56c641d29d032 ">
  <div class="slides">
    <figure class="slide" id="bna-1">
      <div class="slide-content">
        <pre>
 ________________________
|                        |
|                        |
|         0    0         |
|                        |
|       \________/       |
|                        |
|________________________|
</pre>
      </div>
    </figure>
    <figure class="slide" id="bna-2">
      <div class="slide-content">
        <pre>
 ________________________
|                        |
|                        |
|    o              O    |
|                        |
|                        |
|         ______/        |
|________________________|
</pre>
      </div>
    </figure>
    <figure class="slide" id="bna-3">
      <div class="slide-content">
        <pre>
 ________________________
|                        |
|                        |
|       ^       ^        |
|                        |
|                        |
|        (EEEEE)         |
|________________________|
</pre>
      </div>
    </figure>
    <figure class="slide" id="bna-4">
      <div class="slide-content">
        <pre>
 ________________________
|                        |
|                        |
|       |       |        |
|     ____________       |
|     \          /       |
|      \________/        |
|________________________|
</pre>
      </div>
    </figure>
  </div>
  <!-- /.slides  -->
</div>
<!-- /#smile-gallery -->
<p>
  Taken from <a href="https://github.com/leemark/better-simple-slideshow" target="_blank">A Better Simple Slideshow</a>.
</p>

关于javascript - 如何防止 setInterval() 倍增?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35496761/

相关文章:

javascript - 以编程方式预先选择 Material-UI 数据网格中的一行(React)

javascript - 如何为 highchart 图表添加框阴影?

javascript - 如果我在 JavaScript 中创建一个新对象,不是会为该函数的所有属性分配新内存吗?

javascript - Kendo 网格性能问题(Kendo Grid + Angular JS + Web API)

javascript - 在不改变鼠标位置的情况下从 JS 执行 mouseover()

javascript - redux-sagas 生成器产量在回调内失败

javascript - 在指定之前不要继续 Javascript for 循环

javascript - 如何中断输入的 javascript 操作并开始新操作

javascript - 使用 Chart.js 显示图表

javascript - javascript 和 c# 在将数组传递给函数并在函数内部替换它时的区别