函数调用之间的javascript延迟数组中的每个索引
目标:
- 对数组中的每个元素执行逻辑。
- 在下次执行之间等待
X
ms。 -
mouseover(#slider)
暂停延迟 – 如果延迟= 1000ms,并且300ms已经过去,mouseout(#slider)
将触发恢复倒计时剩余的700ms延迟。 - 在最后一个元素上执行后,循环返回再次执行 – 永远。
这是一个直观的解释:
var = s Array(1,2,3) var x = s[1]; //get first element console.log(x); //do something to it wait(); //START wait timer 1000ms //------------> timer : 300ms //------------> user : mouseover (#slider) : pause timer //------------> user : waited 5000ms //------------> user : mouseout (#slider) : resume timer //------------> timer : 300ms --> still 700ms to go! //------------> timer : 500ms //------------> user : mouseover (#slider) : pause timer //------------> user : waited 10000ms //------------> user : mouseout (#slider) : resume timer //------------> timer : 500ms --> still 500ms to go! var x = s[2]; //get second element console.log(x); //do something to it wait(); //START wait timer 1000ms //------------> timer : 200ms //------------> user : mouseover (#slider) : pause timer //------------> user : onclick (.slideButton1) : change index of array and clear timer //------------> user : waited 6000ms //------------> user : mouseout (#slider) : resume timer //------------> timer : 0ms --> still 1000ms to go! var x = s[1]; //get first element ( index was changed by clicking button ) console.log(x); //do something to it wait(); //START wait timer 1000ms // ... s[2] ... s[3] ... //theres nothing else in the array, lets start back from s[1] again!
解决方案:
jQuery的:
http://jsfiddle.net/zGd8a/8/
这个解决方案来自一个相关的post 。 这个插件的官方来源可以在这里找到。
原生JS:
http://jsfiddle.net/SyTFZ/4/
Aadit M Shah的 回答非常有帮助。 他还详细介绍了Delta Timing以及它在类似情况下的用途。
新目标:
摘要这两种方法中的任何一种都可以用于其他事情。
好吧,我不使用jquery,并且很可能我不知道你想要实现的目标。 但是根据我的理解,我认为你应该这样做:
var i = 0; var t = this; var timer = new DeltaTimer(function (time) { // your animation var x = ts[i]; x.delay("1000").css("background-color", "#FAAF16"); delete ts[i]; tspush(x); // increment i i++; }, 1000); var start = timer.start();
你会注意到我使用了一个名为DeltaTimer
的构造函数。 此构造函数在此要点中定义。 它允许您使用start
和stop
function精确控制动画。 传递的render
函数给出一个time
参数,即Date
。 表达式time - start
给出调用函数的确切时间(例如2000
,…)。
使用DeltaTimer
不是setTimeout
或setInterval
的优点是:
- 它纠正了自己 。 这意味着动画更平滑,滞后更少。
- 可以通过启动和停止计时器来控制动画。
- 函数调用的确切时间传递给函数。 这有助于跟踪正在渲染的帧,渲染精灵的位置等。
- 动画的逻辑与时序控制的逻辑分开。 因此代码更具凝聚力,更松散耦合。
您可以在此处 , 此处和此处阅读有关增量计时的其他答案。
编辑1:这实际上非常简单。 我们只是移出数组的第一个元素,处理它然后在最后将其推回。 这是逻辑:
function loopIterate(array, callback, interval) { var timer = new DeltaTimer(function (time) { var element = array.shift(); callback(element, time - start); array.push(element); }, interval); var start = timer.start(); };
现在我们可以创建一个数组并循环遍历它,如下所示:
var body = document.body; loopIterate([1, 2, 3], function (element, time) { body.innerHTML += element + ": " + time + "
"; }, 1000);
你可以在这里看到输出: http : //jsfiddle.net/aGQfr/
编辑2:哎呀,我发现了一个问题。 根据我的理解,您希望在当前元素处理完毕 后的一定时间内处理下一个元素。 我的delta计时脚本不会这样做。 它只以固定的时间间隔执行function。
因此,您根本不需要增量计时。 您需要在处理setTimeout
每个元素后调用setTimeout
:
function loopIterate(array, callback, interval) { var start = + new Date; process(); function process() { var element = array.shift(); callback(element, new Date - start); array.push(element); setTimeout(process, interval); } };
之后,只需创建一个数组并循环遍历它,如下所示:
loopIterate([1, 2, 3], function (element, time) { alert(element); }, 1000);
你可以在这里看到演示(注意你的浏览器可能不喜欢它): http : //jsfiddle.net/aGQfr/1/
编辑3:您还可以组合方法一和二,以便您有一个脚本:
- 在将下一个元素添加到事件队列之前等待处理完成。
- 可以使用
start
和stop
function进行控制。 - 给出回调被调用的确切时间。
- 将处理与时序控制分开。
我们将创建一个名为LoopIterator
的构造函数,它返回一个带有start
和stop
方法的迭代器对象:
function LoopIterator(array, callback, interval) { var start, iterate, timeout; this.start = function () { if (!iterate) { start = + new Date; iterate = true; loop(); } }; this.stop = function () { if (iterate) { clearTimeout(timeout); iterate = false; } }; function loop() { var element = array.shift(); callback(element, new Date - start); array.push(element); if (iterate) timeout = setTimeout(loop, interval); } }
现在我们可以创建并启动一个新的迭代器,如下所示:
var iterator = new LoopIterator([1, 2, 3], function (element, time) { alert(element); }, 3000); iterator.start();
如果您希望,当鼠标分别移动到元素上或元素外时,您甚至可以停止并启动迭代器:
var div = document.getElementsByTagName("div")[0]; div.addEventListener("mouseout", iterator.start, false); div.addEventListener("mouseover", iterator.stop, false);
停止时,迭代器的状态将被保留,再次启动时,它将从停止的位置继续。
您可以在这里看到演示: http : //jsfiddle.net/PEcUG/
编辑4:所以你想创建一个简单的滑块? 让我们从HTML开始,然后是CSS,然后是JavaScript。
HTML:
我们有一个带有一个名为slider
的类的div
元素(因为页面上可能有多个滑块)。 每个滑块都有零个或多个div
元素,类slide
。 每张幻灯片可以包含任意内容。 滑块也有按钮,但我们不在HTML中包含它,因为它将由JavaScript自动生成。 没有冗余。 另请注意,没有任何幻灯片是手动编号的。 一切都由JavaScript处理。
CSS:
.slide { background-color: #EEEEEE; -moz-border-radius: 0.25em; -webkit-border-radius: 0.25em; border-radius: 0.25em; display: none; padding: 1em; } .slider-button { background-color: #CCCCCC; -moz-border-radius: 0.25em; -webkit-border-radius: 0.25em; border-radius: 0.25em; cursor: pointer; float: right; height: 1.25em; margin: 0.5em; width: 1.25em; }
您可以提供任意CSS以满足您的口味。 但重要的一点是.slide
必须有display: none;
因为最初必须隐藏幻灯片。 另外.slider-button
必须有float: right;
。 这很重要,因为向右浮动的元素的顺序可以颠倒。 因此,第一个按钮实际上是最后一个按钮。 这必须由JavaScript正确处理,因此除非您知道自己在做什么,否则不要更改它。
JavaScript:
好吧,我会自下而上解释一下:
window.addEventListener("DOMContentLoaded", function () { var sliders = document.querySelectorAll(".slider"); var length = sliders.length; for (var i = 0; i < length; i++) new Slider(sliders[i], 2000); }, false);
这里Slider
是一个构造函数,它初始化并启动它传递的滑块元素。 它接受两个幻灯片之间的时间间隔作为第二个参数。 这是Slider
的代码:
function Slider(slider, interval) { var slides = slider.querySelectorAll(".slide"); var iterate, start, timeout, delay = interval; slides = Array.prototype.slice.call(slides); var buttons = [], numbers = [], goto = []; var length = slides.length; for (var i = 0; i < length; i++) { var button = document.createElement("div"); button.setAttribute("class", "slider-button"); slider.appendChild(button); buttons.unshift(button); numbers.push(i + 1); var handler = getHandler(length - i); button.addEventListener("click", handler, false); goto.unshift(handler); } this.goto = function (index) { var gotoSlide = goto[index]; if (typeof gotoSlide === "function") gotoSlide(); }; slider.addEventListener("mouseover", stop, false); slider.addEventListener("mouseout", start, false); this.start = start; this.stop = stop; showSlide(); start(); function start() { if (!iterate) { iterate = true; start = + new Date; timeout = setTimeout(loop, delay); } } function stop() { if (iterate) { iterate = false; clearTimeout(timeout); delay = interval - new Date + start; } } function loop() { hideSlide(); slideSlider(); showSlide(); if (iterate) { start = + new Date; timeout = setTimeout(loop, interval); } } function hideSlide() { slides[0].style.display = "none"; buttons[0].style.backgroundColor = "#CCCCCC"; } function slideSlider() { slides.push(slides.shift()); buttons.push(buttons.shift()); numbers.push(numbers.shift()); } function showSlide() { slides[0].style.display = "block"; buttons[0].style.backgroundColor = "#FAAF16"; } function getHandler(number) { return function () { hideSlide(); while (numbers[0] !== number) slideSlider(); showSlide(); }; } }
代码非常明显。 Slider
每个实例都有一个start
, stop
和goto
方法,可以进行更精细的控制。 goto
方法采用幻灯片索引号。 对于n
幻灯片,索引范围从0
到n - 1
。 而已。
滑块的演示在这里: http : //jsfiddle.net/SyTFZ/4/
好了,既然你已经完全简化了问题,这里是一个通用的数组迭代器函数,它在数组的每个元素的迭代之间放置一个延迟,并且它会永远循环,直到回调函数返回false
:
function iterateArrayWithDelay(arr, delay, fn) { var index = 0; function next() { // protect against empty array if (!arr.length) { return; } // see if we need to wrap the index if (index >= arr.length) { index = 0; } // call the callback if (fn(arr[index], arr, index) === false) { // stop iterating return; } ++index; // schedule next iteration setTimeout(next, delay); } // start the iteration next(); }
而且,对于您的示例,您可以像这样使用它:
iterateArrayWithDelay(s, 1000, myFunction);
将myFunction
定义为处理每个元素的回调函数。 回调传递了三个项目:
myFunction(item, array, i){ // your code here to process item }
.delay()
仅适用于使用动画队列的jQuery方法。 在你的代码示例中, .delay('1000')
没有做任何事情,因为在同一个对象之后没有jQuery动画方法。
至于内存泄漏,很难跟踪你正在做的事情的整体上下文,因为我们无法看到this
及其属性所代表的对象的生命周期。 这个序列看起来很奇怪:
var x = ts[i]; ... delete ts[i]; tspush(x);
特别是,我没有看到delete
语句实际上是如何做任何事情的,因为你仍然有对x
内容的引用,所以什么都不会被垃圾回收。 此外,javascript中的delete
用于delete
对象属性,而不是用于释放对象或删除数组元素。 要释放一个对象,你必须摆脱对该对象的所有引用(将它们设置为其他值,以便它们不再包含引用或让它们超出范围)。 因此,既然你永远不会摆脱对ts[i]
任何东西的引用,那么什么都不会被释放。
您使用setTimeout()
不会导致递归。 当您调用setTimeout()
,它会设置一个计时器并将函数引用放入与该计时器关联的数据结构中。 然后,调用函数继续运行并完成它的执行。 因此,它在setTimeout()触发之前完成执行并再次调用它。 所以,它实际上不是递归。 它是一系列顺序函数调用,以时间间隔分隔,一个在下一个可以运行之前完成(因为javascript是单线程的,因为计时器是为将来设置的)。
插件jquery-timing可以帮助您使用非常短的代码完成此效果。 已经有一个等待鼠标hover的例子 。
我相信这也可以适应您的用例:
function noHover() { return this.is(':hover') ? this.wait('mouseleave') : this; } $('.images').repeat().each($).fadeIn($).wait(1000).wait(noHover).fadeOut($);