63 votes

Animer la toile pour ressembler au bruit de la télévision

J'ai une fonction nommée generateNoise() qui crée un élément canvas et peint des valeurs RGBA aléatoires dessus; ce qui donne l'apparence de bruit.


Ma Question

Quel serait le meilleur moyen d'animer indéfiniment le bruit pour donner l'apparence de mouvement. Afin qu'il ait plus de vie?


JSFiddle

function generateNoise(opacite) {
    if(!!!document.createElement('canvas').getContext) {
        return false;
    }
    var canvas = document.createElement('canvas'),
        ctx = canvas.getContext('2d'),
        x,y,
        r,g,b,
        opacite = opacite || .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 + ',' + opacite + ')';
                ctx.fillRect(x,y,1,1);

            }
        }
        document.body.style.backgroundImage = "url(" + canvas.toDataURL("image/png") + ")";

}
generateNoise(.8);

1 votes

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

1 votes

Vous pouvez réduire vos appels Math.random et Math.floor en faisant quelque chose comme x = Math.floor(Math.random() * 0xffffff); r = x & 0xff; g = (x & 0xff00) >>> 8; b = (x & 0xff0000) >>> 16;

0 votes

Quelques conseils: 1) !!!document.createElement('canvas').getContext en !document.createElement('canvas').getContext 2) mettre en place des instructions conditionnelles pour que les utilisateurs qui n'ont pas de canvas ne reçoivent pas d'erreurs 3) ajouter des espaces entre les virgules et le texte qui suit, et un espace entre (param) { pour les fonctions

78voto

Ken Fyrstenberg Points 38115

Mise à jour 1/2017 : J'ai réécrit l'intégralité de la réponse car elle commençait à devenir plutôt confuse, et pour aborder certains des problèmes soulignés dans les commentaires. La réponse originale peut être trouvée ici. La nouvelle réponse contient essentiellement le même code mais amélioré, avec quelques nouvelles techniques, dont une utilise une nouvelle fonctionnalité disponible depuis la publication de cette réponse.


Pour un look "vraiment" aléatoire, nous aurions besoin d'utiliser un rendu au niveau des pixels. Nous pouvons optimiser cela en utilisant des tampons non signés sur 32 bits au lieu de 8 bits, et nous pouvons également désactiver le canal alpha dans les navigateurs plus récents, ce qui accélère l'ensemble du processus (pour les anciens navigateurs, nous pouvons simplement définir un fond opaque noir pour l'élément canvas).

Nous créons un objet ImageData réutilisable une fois en dehors de la boucle principale, de sorte que le coût principal est uniquement lié à putImageData() et pas les deux à l'intérieur de la boucle.

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

/* pour les navigateurs sans prise en charge du canal alpha 2d */
#c {background:#000}

Une façon très efficace, au prix de quelques ressources mémoire mais avec un coût réduit sur le processeur, est de pré-rendre un canvas hors écran plus grand avec le bruit une fois, puis placer ce canvas dans le principal en utilisant des décalages entiers aléatoires.

Il faut quelques étapes de préparation supplémentaires, mais la boucle peut s'exécuter entièrement sur le GPU.

var w = c.width;
var h = c.height;
var ocanvas = document.createElement("canvas");     // crée un canvas hors écran
ocanvas.width = w<<1;                               // défini la taille du canvas hors écran x2
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

// rendre le bruit une fois, sur le canvas hors écran
noise(octx);

// la boucle principale dessine le canvas hors écran à des décalages aléatoires
var ctx = c.getContext("2d", {alpha: false});
(function loop() {
  var x = (w * Math.random())|0;                    // forcer des valeurs entières pour la position
  var y = (h * Math.random())|0;

  ctx.drawImage(ocanvas, -x, -y);                   // dessine le bruit statique (jeu de mots voulu)
  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);
}

/* pour les navigateurs sans prise en charge du canal alpha 2d */
#c {background:#000}

Il est cependant important de noter qu'avec cette dernière technique, vous pourriez risquer d'obtenir des "gels" où le nouveau décalage aléatoire est similaire au précédent. Pour contourner ce problème, définissez des critères pour la position aléatoire afin de ne pas autoriser des positions trop proches à la suite.

11voto

John Bupit Points 1454

J'ai essayé de créer une fonction similaire il y a quelque temps. J'ai attribué une valeur aléatoire à chaque pixel, et en plus de cela, j'ai superposé une onde sinusoïdale qui se déplace vers le haut avec le temps juste pour le rendre plus réaliste. Vous pouvez jouer avec les constantes de l'onde pour obtenir différents effets.

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); // Une onde sinusoïdale de la forme sin(ax + bt)
      pix[i] = pix[i+1] = pix[i+2] = 40 * Math.random() * c; // Définir un gris aléatoire
      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);

Je l'ai utilisé comme préchargeur sur un site. J'ai également ajouté un bouton de volume en tant que barre de chargement, voici une capture d'écran:

Capture d'écran du bruit de télévision

9voto

Paul S. Points 20115

J'ai réécrit votre code de sorte que chaque étape soit séparée afin que vous puissiez réutiliser les choses sans avoir à les recréer à chaque fois, réduire les appels en boucle et j'espère avoir rendu suffisamment clair pour pouvoir le suivre en le lisant.

function generateNoise(opacité, h, w) {
    function makeCanvas(h, w) {
         var canvas = document.createElement('canvas');
         canvas.height = h;
         canvas.width = w;
         return canvas;
    }

    function randomiser(data, opacité) { // voir révision précédente pour 8 bits
        var i, x;
        for (i = 0; i < data.length; ++i) {
            x = Math.floor(Math.random() * 0xffffff); // RGB aléatoire
            data[i] =  x | opacité; // régler tout de RGBA pour un pixel en une fois
        }
    }

    function initialiser(opacité, h, w) {
        var canvas = makeCanvas(h, w),
            contexte = canvas.getContext('2d'),
            image = contexte.createImageData(h, w),
            data = new Uint32Array(image.data.buffer);
        opacité = Math.floor(opacité * 0x255) << 24; // rendre possible calcul booleen OR
        return function () {
            randomiser(data, opacité); // pourrait être sur place pour moins de va-et-vient
            contexte.putImageData(image, 0, 0);
            // vous pouvez envisager d'autres façons de définir le canvas
            // comme l'arrière-plan pour pouvoir le retirer de la boucle, aussi
            document.body.style.backgroundImage = "url(" + canvas.toDataURL("image/png") + ")";
        };
    }

    return initialiser(opacité || 0.2, h || 55, w || 55);
}

Maintenant, vous pouvez créer une boucle d'intervalle ou de temporisation qui ré-invite la fonction générée.

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

Ou avec requestAnimationFrame comme dans la réponse de Ken

var bruit = generateNoise(.8, 200, 200);

(function loop() {
    bruit();
    requestAnimationFrame(loop);
})();

DEMO

8voto

FogleBird Points 23405

La réponse de Ken avait l'air assez bonne, mais après avoir regardé quelques vidéos de vraie statique TV, j'ai eu quelques idées et voici ce à quoi je suis arrivé (deux versions) :

http://jsfiddle.net/2bzqs/

http://jsfiddle.net/EnQKm/

Résumé des changements :

  • Au lieu que chaque pixel soit assigné une couleur de manière indépendante, un ensemble de multiples pixels auront une seule couleur, ce qui donne de courtes lignes horizontales de tailles variables.
  • J'applique une courbe gamma (avec Math.pow) pour orienter légèrement la couleur vers le noir.
  • Je n'applique pas la gamma dans une "zone de bande" pour simuler le banding.

Voici la partie principale du code :

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

6voto

Mouseroot Points 188

Je viens d'écrire un script qui fait exactement cela, en obtenant les pixels d'un canvas noir et en modifiant simplement des valeurs alpha aléatoires en utilisant putImageData

Le résultat peut être trouvé à 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);
            //Générer du bruit statique
            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);
            //Dessiner 'Pas de flux vidéo'
            screenObject.fillStyle = "black";
            screenObject.font = "30pt consolas";
            screenObject.fillText("Pas de flux vidéo",100,250,500);
        }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X