【问题标题】:Pattern to do infinite animation in javascript without stack overflow在没有堆栈溢出的情况下在 javascript 中执行无限动画的模式
【发布时间】: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 大于最大堆栈深度,就会发生堆栈溢出。

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

【问题讨论】:

    标签: javascript html animation canvas infinite


    【解决方案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 和调用函数都会在指定函数执行之前返回。因此调用堆栈在指定函数执行之前被清除。
    • 在我更新代码以包含下次运行的回调之前,这是正确的答案。回调是递归循环函数,所以不能再这样展开了。
    猜你喜欢
    • 2012-05-01
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-23
    • 2014-12-25
    • 1970-01-01
    • 2011-07-17
    相关资源
    最近更新 更多