Animating canvas to look like tv noise

前端 未结 7 2144
野趣味
野趣味 2020-12-22 17:22

I have a function named generateNoise() which creates a canvas element and paints random RGBA values to it; which, gives the appearance of noise.


<
7条回答
  •  一个人的身影
    2020-12-22 18:01

    Update 1/2017: I rewrote the entire answer as it started to become rather messy, and to address some of the issues pointed out in the comments. The original answer can be found here. The new answer has in essence the same code but improved, and with a couple of new techniques, one utilizes a new feature available since this answer was first posted.


    For a "true" random look we would need to use pixel-level rendering. We can optimize this using 32-bit unsigned buffers instead of 8-bit, and we can also turn off the alpha-channel in more recent browsers which speeds up the entire process (for older browsers we can simply set a black opaque background for the canvas element).

    We create a reusable ImageData object once outside the main loop so the main cost is only putImageData() and not both inside the loop.

    var ctx = c.getContext("2d", {alpha: false});       // context without alpha channel.
    var idata = ctx.createImageData(c.width, c.height); // create image data
    var buffer32 = new Uint32Array(idata.data.buffer);  // get 32-bit view
    
    (function loop() {
      noise(ctx);
      requestAnimationFrame(loop)
    })()
    
    function noise(ctx) {
      var len = buffer32.length - 1;
      while(len--) buffer32[len] = Math.random() < 0.5 ? 0 : -1>>0;
      ctx.putImageData(idata, 0, 0);
    }
    /* for browsers wo/2d alpha disable support */
    #c {background:#000}

    A very efficient way, at the cost of some memory but reduced cost on the CPU, is to pre-render a larger off-screen canvas with the noise once, then place that canvas into the main one using random integer offsets.

    It require a few extra preparation steps but the loop can run entirely on the GPU.

    var w = c.width;
    var h = c.height;
    var ocanvas = document.createElement("canvas");     // create off-screen canvas
    ocanvas.width = w<<1;                               // set offscreen canvas x2 size
    ocanvas.height = h<<1;
    
    var octx = ocanvas.getContext("2d", {alpha: false});
    var idata = octx.createImageData(ocanvas.width, ocanvas.height);
    var buffer32 = new Uint32Array(idata.data.buffer);  // get 32-bit view
    
    // render noise once, to the offscreen-canvas
    noise(octx);
    
    // main loop draw the offscreen canvas to random offsets
    var ctx = c.getContext("2d", {alpha: false});
    (function loop() {
      var x = (w * Math.random())|0;                    // force integer values for position
      var y = (h * Math.random())|0;
      
      ctx.drawImage(ocanvas, -x, -y);                   // draw static noise (pun intended)
      requestAnimationFrame(loop)
    })()
    
    function noise(ctx) {
      var len = buffer32.length - 1;
      while(len--) buffer32[len] = Math.random() < 0.5 ? 0 : -1>>0;
      ctx.putImageData(idata, 0, 0);
    }
    /* for browsers wo/2d alpha disable support */
    #c {background:#000}

    Do note though that with the latter technique you may risk getting "freezes" where the new random offset is similar to the previous one. To work around this problem, set criteria for the random position to disallow too close positions in a row.

提交回复
热议问题