animationcanvas看起来像电视噪音

我有一个名为generateNoise()的函数,它创build一个canvas元素,并绘制随机的RGBA值; 这给出了噪音的外观。


我的问题

什么是最好的方式无限制地animation的噪音给运动的外观。 这样可能会有更多的生命?


的jsfiddle

 function generateNoise(opacity) { if(!!!document.createElement('canvas').getContext) { return false; } var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), x,y, r,g,b, opacity = opacity || .2; canvas.width = 55; canvas.height = 55; for (x = 0; x < canvas.width; x++){ for (y = 0; y < canvas.height; y++){ r = Math.floor(Math.random() * 255); g = Math.floor(Math.random() * 255); b = Math.floor(Math.random() * 255); ctx.fillStyle = 'rgba(' + r + ',' + b + ',' + g + ',' + opacity + ')'; ctx.fillRect(x,y,1,1); } } document.body.style.backgroundImage = "url(" + canvas.toDataURL("image/png") + ")"; } generateNoise(.8); 

更新1/2017我重写了整个答案,因为它开始变得相当混乱,并解决了评论中指出的一些问题。 原始答案可以在这里find。 新的本质上是相同的代码,但改进,并与一些新的技术,一个利用了一个新的function,因为这个答案是第一次发布。


对于“真正的”随机外观,我们需要使用像素级渲染。 我们可以使用32位而不是8位的无符号缓冲区来优化,我们也可以closures更新的浏览器中的alpha通道,以加速整个过程(对于旧版浏览器,我们可以简单地设置一个黑色的不透明背景canvas元素)。

我们在主循环之外创build一个可重用的ImageData对象,所以主要成本只有putImageData()而不是在循环内部。

 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} 
 <canvas id=c width=640 height=320></canvas> 

前段时间我试图做类似的function。 我设置每个像素的随机值,除此之外,我叠加了一个随时间向上传播的正弦波,使其看起来更逼真。 你可以使用wave中的常量来获得不同的效果。

 var canvas = null; var context = null; var time = 0; var intervalId = 0; var makeNoise = function() { var imgd = context.createImageData(canvas.width, canvas.height); var pix = imgd.data; for (var i = 0, n = pix.length; i < n; i += 4) { var c = 7 + Math.sin(i/50000 + time/7); // A sine wave of the form sin(ax + bt) pix[i] = pix[i+1] = pix[i+2] = 40 * Math.random() * c; // Set a random gray pix[i+3] = 255; // 100% opaque } context.putImageData(imgd, 0, 0); time = (time + 1) % canvas.height; } var setup = function() { canvas = document.getElementById("tv"); context = canvas.getContext("2d"); } setup(); intervalId = setInterval(makeNoise, 50); 
 <canvas id="tv" width="400" height="300"></canvas> 

我重写了你的代码,所以每一步都是分开的,所以你可以重新使用,而不必每次都创build和重新创build,减less了循环调用,希望能够清晰地阅读。

 function generateNoise(opacity, h, w) { function makeCanvas(h, w) { var canvas = document.createElement('canvas'); canvas.height = h; canvas.width = w; return canvas; } function randomise(data, opacity) { // see prev. revision for 8-bit var i, x; for (i = 0; i < data.length; ++i) { x = Math.floor(Math.random() * 0xffffff); // random RGB data[i] = x | opacity; // set all of RGBA for pixel in one go } } function initialise(opacity, h, w) { var canvas = makeCanvas(h, w), context = canvas.getContext('2d'), image = context.createImageData(h, w), data = new Uint32Array(image.data.buffer); opacity = Math.floor(opacity * 0x255) << 24; // make bitwise OR-able return function () { randomise(data, opacity); // could be in-place for less overhead context.putImageData(image, 0, 0); // you may want to consider other ways of setting the canvas // as the background so you can take this out of the loop, too document.body.style.backgroundImage = "url(" + canvas.toDataURL("image/png") + ")"; }; } return initialise(opacity || 0.2, h || 55, w || 55); } 

现在,您可以创build一些间隔或超时循环,不断重新调用生成的函数。

 window.setInterval( generateNoise(.8, 200, 200), 100 ); 

或者在Ken的答案中使用requestAnimationFrame

 var noise = generateNoise(.8, 200, 200); (function loop() { noise(); requestAnimationFrame(loop); })(); 

DEMO

我恰巧刚刚写了一个脚本来做这件事,通过从一个黑色的canvas获取像素,只是改变随机的alpha值,并使用putImageData

结果可以在http://mouseroot.github.io/Video/index.htmlfind

var currentAnimationFunction = staticScreen

var screenObject = document.getElementById("screen").getContext("2d");

var pixels = screenObject.getImageData(0,0,500,500);

 function staticScreen() { requestAnimationFrame(currentAnimationFunction); //Generate static for(var i=0;i < pixels.data.length;i+=4) { pixels.data[i] = 255; pixels.data[i + 1] = 255; pixels.data[i + 2] = 255; pixels.data[i + 3] = Math.floor((254-155)*Math.random()) + 156; } screenObject.putImageData(pixels,0,0,0,0,500,500); //Draw 'No video input' screenObject.fillStyle = "black"; screenObject.font = "30pt consolas"; screenObject.fillText("No video input",100,250,500); } 

Ken的回答看起来不错,但是在看了一些真正的静态电视的video之后,我有了一些想法,下面是我提出的(两个版本):

http://jsfiddle.net/2bzqs/

http://jsfiddle.net/EnQKm/

变更摘要:

  • 而不是每个像素被独立分配一个颜色,一个多个像素的运行将获得单一的颜色,所以你得到这些短,可变大小的水平线。
  • 我应用一个伽马曲线(与Math.pow)偏向黑色一点的颜色。
  • 我不会在“band”区域应用gamma来模拟banding。

这是代码的主要部分:

 var w = ctx.canvas.width, h = ctx.canvas.height, idata = ctx.createImageData(w, h), buffer32 = new Uint32Array(idata.data.buffer), len = buffer32.length, run = 0, color = 0, m = Math.random() * 6 + 4, band = Math.random() * 256 * 256, p = 0, i = 0; for (; i < len;) { if (run < 0) { run = m * Math.random(); p = Math.pow(Math.random(), 0.4); if (i > band && i < band + 48 * 256) { p = Math.random(); } color = (255 * p) << 24; } run -= 1; buffer32[i++] = color; } 

你可以这样做:

window.setInterval('generateNoise(.8)',50);

第二个参数50是以毫秒为单位的延迟。 增加50会减慢速度,反之亦然。

虽然..这将严重影响网页的性能。 如果是我,我会做渲染服务器端,并呈现一些帧迭代和输出为animation的gif。 不完全相同的无限随机性,但将是一个巨大的性能提升和IMO大多数人不会注意到。

我看起来不像真实的电视静态,但它是相似的。 我只是循环遍历canvas上的所有像素,并将随机坐标上的每个像素的RGB颜色分量更改为随机颜色。 演示可以在CodePen上find。

代码如下:

 // Setting up the canvas - size, setting a background, and getting the image data(all of the pixels) of the canvas. canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); canvas.width = 400; canvas.height = 400; canvasData = ctx.createImageData(canvas.width, canvas.height); //Event listeners that set the canvas size to that of the window when the page loads, and each time the user resizes the window window.addEventListener("load", windowResize); window.addEventListener("resize", windowResize); function windowResize(){ canvas.style.width = window.innerWidth + 'px'; canvas.style.height = window.innerHeight + 'px'; } //A function that manipulates the array of pixel colour data created above using createImageData() function setPixel(x, y, r, g, b, a){ var index = (x + y * canvasData.width) * 4; canvasData.data[index] = r; canvasData.data[index + 1] = g; canvasData.data[index + 2] = b; canvasData.data[index + 3] = a; } window.requestAnimationFrame(mainLoop); function mainLoop(){ // Looping through all the colour data and changing each pixel to a random colour at a random coordinate, using the setPixel function defined earlier for(i = 0; i < canvasData.data.length / 4; i++){ var red = Math.floor(Math.random()*256); var green = Math.floor(Math.random()*256); var blue = Math.floor(Math.random()*256); var randX = Math.floor(Math.random()*canvas.width); var randY = Math.floor(Math.random()*canvas.height); setPixel(randX, randY, red, green, blue, 255); } //Place the image data we created and manipulated onto the canvas ctx.putImageData(canvasData, 0, 0); //And then do it all again... window.requestAnimationFrame(mainLoop); }