我有一个名为generateNoise()
的函数,它创建一个canvas元素并为其绘制随机的RGBA值;这,给出了噪音的外观。
什么是无限制动噪音的最佳方式,以给出运动的外观。那它可能有更多的生命?
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);
答案 0 :(得分:70)
更新1/2017 :我重写了整个答案,因为它开始变得相当混乱,并解决了评论中指出的一些问题。可以找到原始答案here。新版本实际上具有相同的代码,但有所改进,并且使用了一些新技术,其中一个使用了新的功能,因为这个答案是第一次发布的。
<小时/> 对于“真正的”随机外观,我们需要使用像素级渲染。我们可以使用32位无符号缓冲区而不是8位缓冲区来优化它,我们也可以在更新的浏览器中关闭alpha通道,从而加快整个过程(对于旧版浏览器,我们可以简单地设置黑色不透明背景画布元素)。
我们在主循环之外创建一个可重用的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>
一种非常有效的方法,以牺牲内存为代价但降低CPU成本,是使用噪声预渲染更大的屏幕外画布,然后放置 使用随机整数偏移量进入主要画布。
它需要一些额外的准备步骤,但循环可以完全在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}
<canvas id=c width=640 height=320></canvas>
请注意,尽管使用后一种技术,您可能会面临新的随机偏移类似于前一个偏移的“冻结”风险。要解决此问题,请将随机位置的条件设置为禁止连续过多的位置。
答案 1 :(得分:9)
我试图在不久前制作类似的功能。我设置了每个像素的随机值,除此之外,我重叠一个随着时间向上移动向上的正弦波,只是为了让它看起来更逼真。您可以使用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>
我将它用作网站上的预加载器。我还添加了一个音量摇杆作为加载栏,这里是screenshot:
答案 2 :(得分:6)
我重新编写了你的代码,所以每一步都是独立的,所以你可以重复使用东西,而不必每次创建和重新创建,减少循环调用,并希望它足够清晰,以便能够通过阅读它。
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);
}
现在您可以创建一些间隔或超时循环,以便不断重新调用生成的函数。
window.setInterval(
generateNoise(.8, 200, 200),
100
);
requestAnimationFrame
var noise = generateNoise(.8, 200, 200);
(function loop() {
noise();
requestAnimationFrame(loop);
})();
答案 3 :(得分:6)
Ken的回答看起来很不错,但在看了一些真实的电视静态视频后,我有了一些想法,这就是我想出来的(两个版本):
<强> http://jsfiddle.net/2bzqs/ 强>
<强> http://jsfiddle.net/EnQKm/ 强>
变更摘要:
以下是代码的主要部分:
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;
}
答案 4 :(得分:5)
我碰巧编写了一个脚本来做这件事,通过从黑色画布获取像素并只是改变随机alpha值并使用putImageData
结果可在http://mouseroot.github.io/Video/index.html
找到 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);
}
答案 5 :(得分:3)
你可以这样做:
window.setInterval('generateNoise(.8)',50);
第二个arg 50
是一个以毫秒为单位的延迟。增加50
会减慢速度,反之亦然。
答案 6 :(得分:3)
我看起来并不像真正的电视静态,但它仍然相似。我只是遍历画布上的所有像素,并将随机坐标中每个像素的RGB颜色分量更改为随机颜色。 The demo can be found over at CodePen.
代码如下:
// 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);
}