画布内存泄漏

Vit*_*.us 2 javascript html5 memory-leaks canvas getimagedata

我试图生成一个随机噪音的画布,但我无法承受以60fps生成整个画布的随机像素,所以我最终在内存中使用临时画布生成一个小的64x64图块,然后使用上下文填充重复模式,让浏览器将这些字节推送到屏幕,而不是使用javascript引擎.

它的速度要快得多,即使在全屏幕上,我也可以在iOS设备上获得稳定的60fps,但是我注意到在几分钟之后fps表示会下降直到它变得很慢.

在这个小提琴我没有使用应该限制为60Hz的requestAnimationFrame,而是我使用自定义循环,在我的macbook上它以500Hz左右开始并迅速减速以强调问题.

http://jsfiddle.net/Victornpb/m42NT/2/

function loop(){
    drawNoise();
}


function drawNoise(){
    var context = canvas.getContext("2d");
    var pattern = context.createPattern(generatePattern(), "repeat");
    context.rect(0,0, canvas.width, canvas.height);
    context.fillStyle = pattern;
    context.fill()
}

//create a on memory canvas to generate a tile with 64x64 pixels of noise and return it
function generatePattern(){

    var canvas = document.createElement("canvas");
    canvas.width = 64;
    canvas.height = 64;
    var context = canvas.getContext("2d");

    var image = context.getImageData(0, 0, canvas.width, canvas.height);
    var imageData = image.data; // here we detach the pixels array from DOM

    var p;
    var pixels = canvas.width*canvas.height;
    while(pixels--){
        p = pixels*4;
        imageData[p+0] = Math.random() >= 0.5 ? 255 : 0; // Red
        imageData[p+1] = Math.random() >= 0.5 ? 255 : 0; // Green
        imageData[p+2] = Math.random() >= 0.5 ? 255 : 0; // Blue
        imageData[p+3] = 255; // Alpha
    }

    image.data = imageData;
    context.putImageData(image, 0, 0);

    return canvas;
}
Run Code Online (Sandbox Code Playgroud)

Gam*_*ist 5

您正在主绘制函数中使用context.rect,而不创建新路径(beginPath).所以你的所有rect子路径都添加了,并且需要在每个帧上重新绘制== >>很快它就太慢了.

== >>在使用rect之前使用beginPath()或使用fillRect.

function drawNoise() {
    var context = canvas.getContext("2d");
    var pattern = context.createPattern(generatePattern(), "repeat");
    context.fillStyle = pattern;
    context.fillRect(0, 0, canvas.width, canvas.height);
}
Run Code Online (Sandbox Code Playgroud)

请注意,您可以通过创建画布并在每次调用generatePattern时创建图像数据来赢得大量时间,而是反复使用相同的imageData.更重要的是,你只能设置一次alpha:

//create a on memory canvas to generate a tile with 64x64 pixels of noise and return it
var generatePattern = (function () {
    var canvas = document.createElement("canvas");
    canvas.width = 64;
    canvas.height = 64;
    var context = canvas.getContext("2d");
    var image = context.getImageData(0, 0, canvas.width, canvas.height);
    var imageData = image.data; // here we detach the pixels array from DOM
    // set the alpha only once.
    var p = 0,
        pixels = canvas.width * canvas.height;
    while (pixels--) {
        imageData[p + 3] = 255; // Alpha
        p += 4;
    }
    var _generatePattern = function () {
        var p = 0;
        var pixels = canvas.width * canvas.height;
        var data = imageData;
        var rnd = Math.random;
        while (pixels--) {
            data[p++ ] = rnd() >= 0.5 ? 255 : 0; // Red
            data[p++ ] = rnd() >= 0.5 ? 255 : 0; // Green
            data[p++ ] = rnd() >= 0.5 ? 255 : 0; // Blue
            p++;
        }
        context.putImageData(image, 0, 0);
        return canvas;
    }
    return _generatePattern;
})();
Run Code Online (Sandbox Code Playgroud)

更新的小提琴在这里:

http://jsfiddle.net/gamealchemist/m42NT/15/

编辑:使用一次调用random()只是为了获得一个随机位是一个过度杀手:使用math.random()获取一个位域,然后在它为空时重新填充该位域.这里我从Math.random()中获取了21位,因为它没有更多有效位.这样,对于相同的结果,您对此函数(!!)的调用次数减少了21倍.

http://jsfiddle.net/gamealchemist/m42NT/18/

//create a on memory canvas to generate a tile with 64x64 pixels of noise and return it
var generatePattern = (function () {
    var canvas = document.createElement("canvas");
    canvas.width = 64;
    canvas.height = 64;
    var context = canvas.getContext("2d");
    var image = context.getImageData(0, 0, canvas.width, canvas.height);
    var imageData = image.data; // here we detach the pixels array from DOM
    // set the alpha only once.
    var p = 0,
        pixels = canvas.width * canvas.height;
    while (pixels--) {
        imageData[p + 3] = 255; // Alpha
        p += 4;
    }
    var _generatePattern = function () {
        var p = 0;
        var pixels = canvas.width * canvas.height;
        var data = imageData;
        var rnd = Math.random;
        var bitsLeft = 0;
        var multiplier = (1<<22)-1;
        var mask = 0;
        while (pixels--) {
            if (!bitsLeft) {
                bitsLeft=21;
                mask= 0 | (Math.random()*multiplier);
            }
            data[p++ ] = (mask & 1) && 255 ; // Red
            data[p++ ] = (mask & 2 )  && 255 ; // Green
            data[p++ ] = (mask & 4) && 255; // Blue
            p++;
            mask>>=3;
            bitsLeft-=3;
        }
        context.putImageData(image, 0, 0);
        return canvas;
    }
    return _generatePattern;
})();
Run Code Online (Sandbox Code Playgroud)