【问题标题】:Using setTimeout func. & random func. to animate class elements使用 setTimeout 函数。 & 随机函数。动画类元素
【发布时间】:2012-01-17 20:21:10
【问题描述】:

我的调试技能并不能帮助我找出我在这里做错了什么。

我希望数组中的每个元素在指定时间后使用 setTimeout 函数进行动画处理。

我没有收到任何错误,并且循环似乎运行得很好,但是,数组中的所有元素最终都没有从原来的位置移动到新位置。

function publicity()
{
// placing elements with class name 'cCameras' inside an array
var eCamerasArray = $(".cCameras").toArray();
// creating 2 arrays to hold left & top values of each element
var iLeftPosArray = [];
var iTopPosArray = [];
// loop to run through each element in array
for( var i = 0; i < eCamerasArray.length; i++)
{
    // timer variable set for each element to be used in setTimeout func.
    var timer = Math.floor (Math.random()*300) + 100;
    // setTimeout func. used to animate each element after a specified (timer) time
    window.setTimeout (function ()
    {
        iLeftPosArray[i] = Math.floor (Math.random() *139) + 360;
        iTopPosArray[i] = Math.floor (Math.random() *160) + 100 ;
        $(eCamerasArray[i]).animate ({left: iLeftPosArray[i] + "px", top: iTopPosArray[i] + "px"}, 100, "linear");
        return [iLeftPosArray[i], iTopPosArray[i]];
    }, timer);
}
}

【问题讨论】:

  • javascript 没有块级范围,因此所有函数都将引用相同的 i.. 有人在此找到重复项:p
  • @Esailija,你完全失去了我,请你详细说明。
  • @Esailija - 他在数组中使用它,我认为他不会对这段代码有这个问题。 Kayote,通过确认eCamerasArray的值开始调试,然后循环后得到iLeftPosArrayiTopPosArray的值。
  • @KevinB ?循环立即执行,一旦超时开始触发,i 将与创建的所有函数的值相同。每天至少有 10 个问题。
  • @KevinB,做到了。 Firebug 实际上通过该函数运行得相当奇怪,但确实运行了所有函数,包括通过 console.log 输出数组正确的内容。 iLeftPosArray & iTopPosArray 也给出了正确的值。

标签: javascript jquery arrays jquery-animate settimeout


【解决方案1】:

你可以通过创建闭包来修复它:

(function publicity() {
    var eCamerasArray = $(".cCameras"),
        iLeftPosArray = [],
        iTopPosArray = [],
        timer;
    for(var i = 0; i < eCamerasArray.length; i += 1) {
        timer = Math.floor (Math.random() * 300) + 100;
        (function (i) {
            window.setTimeout (function () {
                iLeftPosArray[i] = Math.floor (Math.random() * 139) + 360;
                iTopPosArray[i] = Math.floor (Math.random() * 160) + 100 ;
                $(eCamerasArray[i]).animate ({left: iLeftPosArray[i] + "px", top: iTopPosArray[i] + "px"}, 300, "linear");
                return [iLeftPosArray[i], iTopPosArray[i]];
            }, timer);
        }(i));
    }
}());

你可以在这里看到效果:http://jsfiddle.net/zHUAt/2/

最好的问候!

【讨论】:

  • 谢谢。我已经阅读了一些帖子,但从未了解过关闭的窍门。以下是我对您的回答的理解: setTimeout 的函数接受 'i' 参数来跟踪循环编号。当前正在运行。但是,我有点失去了最后一点,在函数的末尾,你有 '}(i));,底部第三行。为什么'i'在这里被相乘?
  • 你可以把它想象成 i 的当前状态保存在函数体内。所以最后你不会调用 i 等于 3 的超时函数。
  • 其实 this (function () { //... }()) 是一个自执行函数的语法。在示例中,该函数是匿名的,它只接受一个参数(即 i 但与循环中的 i 不同,它可能具有不同的值)。我正在使用 '}(i));'因为我想将 i 作为参数传递给自执行匿名函数。这就是它如何将 i 的当前值“保存”到它的主体中。我希望我很清楚:-)。最好的问候!
  • 非常感谢您抽出宝贵的时间。我一直在思考和思考,但在这方面有点挣扎。因此,简而言之,函数 (i) 中的“i”与循环中的上述 i 不同。但是 (i) 乘法与循环中的 'i' 相同...首先是 que,所以这个匿名函数独立于父函数中的其余代码,除了最后的乘法 (i)它。当然,将函数与 (i) 相乘是行不通的,因为您将方法与数字相乘...
  • @Kayote - 不,但您可以使用 (function(y){...})(i) 来获得这种效果
【解决方案2】:

展开一个简单的循环,你可以看到会发生什么:

var i = 0;

window.setTimeout( function(){
      //No local i so it must be outside
    console.log(i);

}, 1000 );

i++;

window.setTimeout( function(){
     //No local i so it must be outside
    console.log(i);

}, 1000 );

i++;

window.setTimeout( function(){
      //No local i so it must be outside
    console.log(i);

}, 1000 );

如你所见,所有函数都引用同一个i,所以 一旦计时器触发,他们都会记录2。他们都没有 有一个本地的i

您可以像这样创建“本地”i

(function(i){
|---------^  //i found here, no need to use the global i
|   window.setTimeout( function(){
-------------------- //no local i here so it must be outside
        console.log(i);

    }, 1000 );  


})(i) //pass the "global" i as argument, with the value it has right now

【讨论】:

  • 嗯,这就是我的解决方案,不是吗?
  • @mgechev 这是任何人的解决方案,这个问题有数百万个形式略有不同的重复项。我只是想解释为什么它不起作用。
  • 这是一个绝妙的解释。感谢您为我澄清这一点@Esailija。
猜你喜欢
  • 1970-01-01
  • 2018-08-11
  • 1970-01-01
  • 2011-10-23
  • 2019-12-10
  • 2010-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多