【问题标题】:Canvas memory leak画布内存泄漏
【发布时间】:2014-08-18 05:06:21
【问题描述】:

我试图生成一个带有随机噪声的画布,但我无法以 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;
}

【问题讨论】:

    标签: javascript html canvas memory-leaks getimagedata


    【解决方案1】:

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

    ==>> 在使用 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);
    }
    

    请注意,您可以通过不创建画布并在每次调用 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;
    })();
    

    更新的小提琴在这里:

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

    【讨论】:

    • 我知道我可以改进,随机使用按位运算,但我没有想到使用相同的 imageData,但我使用了一个函数,我预先计算了一系列随机图块并缓存它,并且每次都保持重播相同的序列,所以我可以用内存和伪随机噪声来换取计算时间。我知道我做错了什么,但我找不到任何关于 api 如何工作的参考,就像每个函数似乎都在做的那样。
    【解决方案2】:

    试试 window.requestAnimationFrame(yourLoopF​​unction);当你调用你的循环函数时。

    function loop(){
        drawNoise();
        window.requestAnimationFrame(loop);
    }
    loop();
    

    【讨论】:

      猜你喜欢
      • 2012-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-18
      • 2016-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多