Подтвердить что ты не робот

Анимированный холст, похожий на шум в телевизоре

У меня есть функция с именем generateNoise(), которая создает элемент холста и рисует случайные значения RGBA; что дает шум.


Мой вопрос

Каким будет лучший способ бесконечно анимировать шум, чтобы дать вид движения. Чтобы он мог иметь больше жизни?


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);
4b9b3361

Ответ 1

Обновление 1/2017. Я переписал весь ответ, поскольку он начал становиться довольно грязным, и для решения некоторых проблем, указанных в комментариях. Оригинальный ответ можно найти здесь. Новый по сути тот же код, но улучшенный, и с помощью нескольких новых методов, используется новая функция, доступная, поскольку этот ответ был впервые опубликован.


Для "истинного" случайного взгляда нам нужно будет использовать рендеринг на уровне пикселей. Мы можем оптимизировать это с использованием 32-но неподписанных буферов вместо 8-разрядных, и мы также можем отключить альфа-канал в более поздних браузерах, что ускоряет весь процесс (для старых браузеров мы можем просто установить черный непрозрачный фон для элемент холста).

Мы создаем объект многократного использования 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>

Ответ 2

Я пытался сделать подобную функцию некоторое время назад. Я установил случайное значение каждого пикселя, и в дополнение к этому я наложил синусоидальную волну, которая со временем продвигалась вверх, чтобы сделать ее более реалистичной. Вы можете играть с константами в волне, чтобы получить разные эффекты.

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>

Ответ 3

Я переписал ваш код так, чтобы каждый шаг был отдельным, чтобы вы могли повторно использовать вещи, не создавая и не создавая каждый раз, сокращал вызовы в цикле и, надеюсь, делал это достаточно ясно, чтобы следить за чтением Это.

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

DEMO

Ответ 4

Кен ответ выглядел неплохо, но, посмотрев несколько видеороликов реального статичного телевизора, у меня были некоторые идеи и вот что я придумал (две версии):

http://jsfiddle.net/2bzqs/

http://jsfiddle.net/EnQKm/

Сводка изменений:

  • Вместо того, чтобы каждый пиксель был независимо назначен цветом, запуск нескольких пикселей будет иметь один цвет, поэтому вы получите эти короткие горизонтальные линии переменного размера.
  • Я применяю гамма-кривую (с помощью Math.pow), чтобы немного смещать цвет в сторону черного.
  • Я не применяю гамма в области "полоса" для имитации полосы.

Здесь основная часть кода:

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

Ответ 5

Я только что написал script, который делает именно это, получая пиксели от черного холста и просто изменяя произвольные значения альфа-и используя putImageData​​p >

Результат можно найти на 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);
        }

Ответ 6

Вы можете сделать это следующим образом:

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

Второй arg 50 - это задержка в миллисекундах. Увеличение 50 замедлит и уменьшит визу.

хотя.. это серьезно повлияет на производительность веб-страницы. Если бы это был я, я бы сделал сервер рендеринга и выполнил несколько итераций фреймов и вывел их в виде анимированного gif. Не совсем то же самое, что и бесконечная случайность, но это будет огромный прирост производительности, и большинство ИМО большинство людей даже не заметят.

Ответ 7

Шахта не похожа на реальную статичность телевизора, но, тем не менее, она похожа. Я просто просматриваю все пиксели на холсте и меняю цветные компоненты RGB каждого пикселя на случайную координату на случайный цвет. Демонстрационная версия может быть найдена в 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);
}