在没有堆栈溢出的情况下在 javascript 中执行无限动画的模式

Posted

技术标签:

【中文标题】在没有堆栈溢出的情况下在 javascript 中执行无限动画的模式【英文标题】:Pattern to do infinite animation in javascript without stack overflow 【发布时间】:2013-02-25 13:57:26 【问题描述】:

我有以下模式重复动画(本身由 x 帧组成), t 次:

sprite.prototype.loop = function(t,animation_name,frame_delay) 
    if(t > 0) 
        function next_run() 
            this.loop(t-1,animation_name,frame_delay);
         
        this.run(animation_name,frame_delay,next_run);
      
    ;

sprite.prototype.run = function(animation_name,frame_delay,frame_count,callback) 
    frame_count ||= 0;
    var s = this;
    if(frame_count < this.paths[animation_name].length)  // x frames
        setTimeout( function () 
            s.blit(s.paths[animation_name][frame_count]);
            s.run(animation_name,frame_delay,frame_count+1);
            , frame_delay );
        
     else 
        if(!!callback) callback();
    

super_mario.loop(10000,'mushroom_death',40);

很明显,如果 x*t 大于最大堆栈深度,就会发生堆栈溢出。

问题:这种模式可以扩展到无限次运行动画的情况,还是有更正确的方法来处理无限循环的情况?

【问题讨论】:

【参考方案1】:

我会重写你的loop 函数如下:

sprite.prototype.loop = function (t, animation_name, frame_delay) 
    var s = this;
    var frames = s.paths[animation_name];
    var x = frames.length;

    for (var i = 0; i < t; i++) 
        for (var frame_count = 0; frame_count < x; frame_count++) 
            setTimeout(function (frame_count) 
                s.blit(frames[frame_count]);
            , (i * x + frame_count) * frame_delay, frame_count);
        
    
;

就是这样。不会有任何堆栈溢出,因为没有递归。

编辑: 正如刚才提到的,您的代码还有另一个问题。两个相同的帧之间不会有任何延迟,因为您实际上为所有相同的帧同时调用setTimeout。因此,无论t 的值是多少,动画都只会出现一次。

【讨论】:

连续设置这么多setTimeouts 是否存在(内存/执行)问题? 存在setTimeout 无法保证在给定延迟时执行操作的问题。相反,渲染器应该查看时间并在requestAnimationFrame 循环中决定它应该在哪个帧上。这样,如果有人要最小化窗口或切换选项卡然后返回,渲染将在正确的位置继续。【参考方案2】:

setTimeout 不继承调用者的堆栈帧,所以你只需要担心 t 在这里,虽然没有理由不能简单地迭代地编写,这样堆栈大小就不是一个关注:

sprite.prototype.loop = function(t, animation_name, frame_delay)
    while (t--)
        this.run(animation_name, frame_delay);
    
;

但是,感觉这段代码并没有按照您的预期进行。这实际上是并行运行动画t 次,因为run 中的setTimeout 调用将相互交错。我不确定我能理解这将是怎样的预期效果,因为每个t 延迟回调都会在此处执行完全相同的blit 操作,因此不会有明显的区别。

【讨论】:

并不是说对setTimeout 的调用没有被压入调用堆栈。每个功能都是。当你调用setTimeout 时会发生什么,它推迟了指定函数的执行。因此,setTimeout 和调用函数都会在指定函数执行之前返回。因此调用堆栈在指定函数执行之前被清除。 在我更新代码以包含下次运行的回调之前,这是正确的答案。回调是递归循环函数,所以不能再这样展开了。

以上是关于在没有堆栈溢出的情况下在 javascript 中执行无限动画的模式的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在不使用 ImageMagick 或其他第三方软件的情况下在 R 中制作 GIF?

执行堆栈操作以在没有集合api的情况下在java中推送字符串

如何在没有 jQuery 的情况下在 JavaScript 中打开 JSON 文件?

如何在没有jQuery的情况下在javascript中执行php文件

html 在没有jquery的情况下在javascript中处理事件的方法。

有没有办法在不使用画布的情况下在 JavaScript 中合并图像?