Firefox的递归费用昂贵

时间:2013-07-01 03:45:48

标签: javascript performance firefox canvas pixel-manipulation

编辑:仅在Firefox中出现! (我使用的是22.0)请参阅底部的浏览器比较。

我试图通过复制像素数据并逐渐将alpha值从255更改为0(背景为黑色)来在画布上创建“淡化为黑色”效果。

function fadeToBlack () {
    if(typeof this.recursion === 'undefined' || this.recursion === 0) {
        this.recursion = 1;
        this.imageData = this.ctx.getImageData(0, 0, this.width, this.height);
        this.imageDataArray = this.imageData.data;
        this.pixelCount = this.imageDataArray.length/4;
        this.fadeToBlack();
    }
    else if (this.recursion <= 15){
        console.time('Change alpha ' + this.recursion);
        for (var i = 0; i < this.pixelCount; i++){
            this.imageDataArray[i * 4 + 3] = 255 - 255 / 15 * this.recursion;
        }
        console.timeEnd('Change alpha ' + this.recursion);
        this.ctx.putImageData(this.imageData, 0, 0);
        this.recursion++;
        setTimeout(function(){
            this.fadeToBlack();
        }.bind(this), 50);
    }
    else {
        this.recursion = 0;
    }
};

我认为这会非常昂贵(1280 * 1024 = 1310720次迭代!),但正如您从下面的控制台日志中看到的那样,除了第一次迭代外,它的速度非常快。

Change alpha 1: 543ms
Change alpha 2: 16ms
Change alpha 3: 6ms
Change alpha 4: 16ms
...

奇怪的是,如果我只是延迟fadeToBlack的第二次迭代(像素操作的第一次迭代)......

function fadeToBlack () {
    if(typeof this.recursion === 'undefined' || this.recursion === 0) {
        this.recursion = 1;
        this.imageData = this.ctx.getImageData(0, 0, this.width, this.height);
        this.imageDataArray = this.imageData.data;
        this.pixelCount = this.imageDataArray.length/4;
        //This is the only difference!
        setTimeout(function(){
            this.fadeToBlack();
        }.bind(this), 0);
    }
    else if (this.recursion <= 15){
        console.time('Change alpha ' + this.recursion);
        for (var i = 0; i < this.pixelCount; i++){
            this.imageDataArray[i * 4 + 3] = 255 - 255 / 15 * this.recursion;
        }
        console.timeEnd('Change alpha ' + this.recursion);
        this.ctx.putImageData(this.imageData, 0, 0);
        this.recursion++;
        setTimeout(function(){
            this.fadeToBlack();
        }.bind(this), 50);
    }
    else {
        this.recursion = 0;
    }
};

发生了一些神奇的事情。

Change alpha 1: 16ms
Change alpha 2: 16ms
Change alpha 3: 6ms
Change alpha 4: 6ms
...

那到底是怎么回事?

编辑:我在几个浏览器中对此进行了测试,以下是所有15次迭代的结果(以毫秒为单位)。

Browser  |Recursive  |Asynchronous
=========+===========+============
Firefox  |1652†      |1136
Chrome   |976        |978
Opera    |12929      |13855
IE       |855        |854

†第一次迭代非常昂贵(500ms)。

1 个答案:

答案 0 :(得分:1)

我认为这会将函数之间的跳转减少一半,因为你只调用一次直到函数死亡(当使用setTimeout使用异步调用时),但是如果你通过从内部调用来使用递归,它将在那时停止并且跳到下一个调用,依此类推,直到它完成最后一次调用,然后继续逐步递归,从它停止的行调用前一个函数继续使用递归的返回值并返回到前一个,我可以查看性能和方法的出价差异。我要问的是,它是否会给你相同的结果,我怀疑情况并非如此。

TL; DR setTimeout:异步调用(独立),递归:同步(依赖)。

图形演示:

enter image description here