如何防止我的setInterval()倍增?

时间:2016-02-19 03:24:12

标签: javascript

我正在修改A Better Simple Slideshow以获得暂停功能。我设法将autoCycle()中的暂停和取消暂停分离为自己的方法(pauseCycle和unpauseCycle),因此我可以将它们用于暂停控制以及autoCycle()。我已经创建了addPause()方法来生成暂停控件。 addPause()方法成功暂停了幻灯片放映,但在取消暂停幻灯片放映时,使用setInterval()或传递给它的间隔var做了一些时髦的事情。它似乎导致另外一个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);
&#13;
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;
}
&#13;
<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>
&#13;
&#13;
&#13;

当你运行这个脚本时,如果你使用Pause控件,会发生一些奇怪的事情。首先,它会暂停并禁用pauseOnHover条件,就像它应该的那样。然后再次单击它,它会取消暂停并开始一次推进两张幻灯片。 pauseOnHover再次起作用,但仅在悬停时暂停其中一个幻灯片前进,因此幻灯片放映仍然一次前进一张幻灯片。单击它再次暂停,它会停止前进两次,但它会继续一次滑动一次(当它应该暂停时)。再次点击它,它开始前进三帧(如果你再次悬停在它上面两个),依此类推。每次都有一些东西不断添加,我无法弄清楚它是什么。请帮忙。

谢谢!

2016年2月22日更新

Here's a JSFiddle我一直在为这个项目而努力。同时暂停和暂停以及暂停工作一直是个噩梦。

2 个答案:

答案 0 :(得分:1)

区间变量需要绑定到适当的范围(this)。那么你在哪里引用变量&#34; interval&#34;你需要在前面添加&#34;这个。&#34;

搜索代码&#34;已更改&#34;看看我做了哪些改变。

&#13;
&#13;
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);
&#13;
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;
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

我明白了!简单回答:

停止倍增!

解决方案#1: “让它运行”

不要停止setInterval()。相反,给它一些变量来检查。如果它为它们中的任何一个返回false,则不要执行幻灯片转换(或者在您的情况下应该执行的任何操作)。 setInterval()仍将运行,每次触发时都会检查这些变量。但是每次触发变量都会返回false,它永远不会执行里面的代码。当发生单击或某些内容更改变量时,当它再次触发时,它将执行内部代码(幻灯片过渡等)。 setInterval()永远不会停止,因此您永远不必再次启动它,您永远不必担心如何选择setInterval()的同一个实例。见下面的例子:

var aFSlides = function (el, options) {
  var $slideshows = document.querySelectorAll(el), // a collection of all of the slideshow
  $slideshow = {},
  Slideshow = {
    init: function (el, options) {
      this.counter = 0; // to keep track of the current slide
      this.interval = 0; // to keep track of whether slideshow is running or not
      this.stopCycle = 0; // to keep track of whether the slideshow has been stopped or not
      this.hovered = 0; // to keep track of whether the mouse is hovering over the slideshow or not
      this.paused = 0; // to keep track of whether the pause control is set to "pause" 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 
      this.opts = {
        auto: (typeof options.auto === "undefined") ? false : options.auto,
        speed: (typeof options.speed === "undefined") ? 6000 : options.speed,
        pauseOnHover: (typeof options.pauseOnHover === "undefined") ? true : options.pauseOnHover,
        pause: (typeof options.pause === "undefined") ? false : options.pause,
      };
      this.$items[0].classList.add('current'); // add show class to first figure 
      if (this.opts.prevNext) {
        this.addPrevNext(this.el);
      }
      if (this.opts.pause) {
        this.addPause(this.el);
      }
      this.addEventListeners(this.el,this.opts.speed);
      if (this.opts.auto) {
        this.unpauseCycle(this.el, this.opts.speed, this.opts.pauseOnHover);
      }
      if (this.opts.fullScreen) {
        this.addFullScreen(this.el);
      }
      if (this.opts.swipe) {
        this.addSwipe(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');
    },
    addPause: function (el) {
      // build and inject prev/next controls
      // first create all the new elements
      var spanPause = document.createElement("span"),
      docFragPs = document.createDocumentFragment();
      // add classes
      spanPause.classList.add('pause');
      // add contents
      spanPause.innerHTML = 'Pause';
      // append elements to fragment, then append fragment to DOM
      docFragPs.appendChild(spanPause);
      el.appendChild(docFragPs);
    },
    unpauseCycle: function (el,speed) {
      var that = this;
      this.interval = setInterval(function () {
        if ((that.stopCycle != 1) && (that.hovered != 1)) {
          that.showCurrent(1); // increment & show
        }
      }, speed);
    },
    addEventListeners: function (el,speed,pauseOnHover) {
      var that = this;
      if (that.opts.pauseOnHover) {
        el.addEventListener('mouseover', function () {
          that.hovered = 1;
          return that.hovered;
        }, false);
        el.addEventListener('mouseout', function () {
          that.hovered = 0;
          return that.hovered;
        }, false);
      };
      el.querySelector('.pause').addEventListener('click', function () {
        if ((that.paused === 0) && (that.stopCycle != 1)) {
          that.stopCycle = 1;
          that.paused = 1;
          el.classList.add('paused');
          return that.stopCycle,that.paused;
        }
        else if ((that.paused === 1) || (that.stopCycle === 1)) {
          that.stopCycle = 0;
          that.paused = 0;
          el.classList.remove('paused');
          el.classList.remove('stopped');
          that.interval;
          return that.stopCycle,that.paused;
        }
      }, false);
    },
  }; // end Slideshow object .....
  // make instances of Slideshow as needed
  [].forEach.call($slideshows, function (el) {
    $slideshow = Object.create(Slideshow);
    $slideshow.init(el, options);
  });
};
var aFS56c641d29d032 = {
  auto: true,
  speed: 2000,
  prevNext: true,
  pause: true,
  swipe: true,
};
aFSlides('.aFS56c641d29d032', aFS56c641d29d032);
body {
    font: 400 14px/1.3 Menlo,Courier,sans-serif;
}
figure {
  display: none;
}
figure.current {
  display: block;
}
figure p {
  font: 700 24px/1.3 Menlo,Courier,sans-serif;
  white-space: pre;
}
span {
  font-weight: bold;
  padding: 10px;
  display: inline-block;
}
<body>
<div id="smile-gallery" class="aFS56c641d29d032 ">
        <div class="slides">
          <figure class="slide" id="bna-1">
            <div class="slide-content">
              <p>
 ________________________
|                        |
|                        |
|         0    0         |
|                        |
|       \________/       |
|                        |
|________________________|
</p></div>
          </figure>
          <figure class="slide" id="bna-2">
            <div class="slide-content">
              <p>
 ________________________
|                        |
|                        |
|    o              O    |
|                        |
|                        |
|         ______/        |
|________________________|
</p></div>
          </figure>
          <figure class="slide" id="bna-3">
            <div class="slide-content">
              <p>
 ________________________
|                        |
|                        |
|       ^       ^        |
|                        |
|                        |
|        (EEEEE)         |
|________________________|
</p></div>
          </figure>
          <figure class="slide" id="bna-4">
            <div class="slide-content">
              <p>
 ________________________
|                        |
|                        |
|       |       |        |
|     ____________       |
|     \          /       |
|      \________/        |
|________________________|
</p></div>
          </figure>
        </div><!-- /.slides  -->
      </div><!-- /#smile-gallery -->
<p>
Taken from <a href="https://github.com/leemark/better-simple-slideshow">A Better Simple Slideshow</a>.
</p>
</body>

解决方案#2: “不要忽视它”

这里的想法是首先为所有内容创建变量,然后使用它们,并且始终在正确的范围内(当然)。所以这是如何使用它们:

定义变量后(在对象的最外层范围内,为了便于编码),将setInterval()分配给其中一个变量(例如myIntervalVar = setInterval( ... );),然后始终用它引用它变量。所以要清除它,clearInterval(myIntervalVar)。确保您的范围正确,因此您可能需要使用:

clearInterval(this.myIntervalVar)

或:

var equalThis = this;
someFunction(){
  clearInterval(equalThis.myIntervalVar);
}

然后,只要您想要启动/停止setInterval(),请先检查它是否已经使用变量运行:

if (this.myRunningVar === 0) {
  this.myIntervalVar;
}

这当然意味着当您启动setInterval()时,您需要设置变量以跟踪它:

myIntervalVar = setInterval( function () {
  ...
  this.myRunningVar = 1;
  return myRunningVar;
}, false);

if (this.myRunningVar === 0) {
  this.myIntervalVar;
  this.myRunningVar = 1;
  return myRunningVar;
}

以及每当你停止它时:

clearInterval(this.myIntervalVar)
this.myRunningVar = 0;
return myRunningVar;

可能会详细阐述第二种解决方案,并且应该进行更彻底的测试。我选择了第一个解决方案,因为它更容易,对我来说更清洁。

基本上,setInterval()同时点火的倍增或堆叠来自运行一次启动它而不成功停止它的函数(或函数)。即使你用clearInterval()成功阻止了它,它仍然在那里(尽管不再射击)。因此,如果你将它分配给一个变量,你需要做的只是调用那个变量,然后它会再次启动,从它停止的地方开始。否则,如果您再次尝试将该变量再次定义为setInterval()函数,请使用您继续调用的函数,而不是使用新的setInterval()重置变量它只是在其上添加另一个setInterval()

startCycle(){
  myIntervalVar = setInterval( function () {
        // whatever you want to execute each time it fires
  }, false);
}

多次调用startCycle()会导致多个setInterval()同时运行,因为每次执行此操作时,会将 setInterval()添加到{{} 1}}。每次设置或调用设置它的功能时,它不仅添加您的新myIntervalVar,还会播放所有旧的setInterval()和新的。{1}}。这就是为什么我更喜欢设置和调用它一次并使用变量来控制是否应该在每次触发时实际执行代码,或者它应该返回false直到其中一个变量发生变化。但假设您使用clearInterval()清除它,您如何重新开始?将其分配给变量的一个关键原因是,您可以再次调用而无需添加另一个setInterval()

reStartCycle(){
  myIntervalVar;
}