質問

EDIT: Only occurs in Firefox! (I'm using 22.0) See the browser comparison at the bottom.

I'm trying to create a 'fade to black' effect on the canvas by copying the pixel data and progressively changing the alpha values from 255 to 0 (the background is black).

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;
    }
};

I thought this would be really expensive (1280 * 1024 = 1310720 iterations!), but as you can see from the console log below, it was surprisingly quick except for the first iteration.

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

Curiously, if I simply delay the second iteration of fadeToBlack (first iteration of the pixel manipulation)...

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;
    }
};

Something magical happens.

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

So what the heck is going on here?

EDIT: I tested this in several browsers and here are the results in milliseconds for all 15 iterations.

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

†First iteration was very expensive (500ms).

役に立ちましたか?

解決

I think that reduces the jumps between the functions by half, as you only call once till the function dies (when using async call using setTimeout), however if you use recursion by calling from within, it will stop at that point and jump to the next call and so on until it finishes the last call then go on recursion gradually to call the previous function from the line it stopped at to continue using the returned value from the recursion and return back to the previous one,, I can see a bid difference in performance and methodology. I've to ask if it gives you the same result which I suspect wouldn't be the case.

TL;DR setTimeout: asynchronous call (independent), recursion: synchronous (dependent).

graphical demonstration:

enter image description here

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top