J'ai une fonction nommée generateNoise()
qui crée un élément canvas et y peint des valeurs RGBA aléatoires; ce qui donne l'apparence du bruit.
Quelle serait la meilleure façon d'animer le bruit à l'infini pour donner l'apparence du mouvement. Pour qu'il ait plus de vie?
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);
Mise à jour 1/2017 : J'ai réécrit toute la réponse car elle commençait à devenir plutôt désordonnée et à résoudre certains des problèmes signalés dans les commentaires. La réponse originale peut être trouvée ici . Le nouveau a essentiellement le même code mais s'est amélioré, et avec quelques nouvelles techniques, on utilise une nouvelle fonctionnalité disponible depuis la première publication de cette réponse.
Nous créons un objet ImageData
réutilisable une fois à l'extérieur de la boucle principale, donc le coût principal est seulement 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);
}
/* for browsers wo/2d alpha disable support */
#c {background:#000}
<canvas id=c width=640 height=320></canvas>
Un moyen très efficace, au prix d'une certaine mémoire mais d'un coût réduit sur le CPU, est de pré-rendre un canevas hors écran plus grand avec le bruit une fois, puis de placer that canevas dans le principal en utilisant des décalages entiers aléatoires.
Cela nécessite quelques étapes de préparation supplémentaires, mais la boucle peut fonctionner entièrement sur le 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>
Notez cependant qu'avec cette dernière technique, vous risquez d'obtenir des "blocages" lorsque 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 pour interdire les positions trop proches dans une rangée.
J'ai essayé de créer une fonction similaire il y a quelque temps. J'ai défini chaque valeur aléatoire de pixel, et en plus de cela, j'ai superposé une onde sinusodiale qui a voyagé vers le haut avec du temps pour le rendre plus réaliste. Vous pouvez jouer avec les constantes de la vague 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); // 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>
Je l'ai utilisé comme préchargeur sur un site. J'ai également ajouté une bascule de volume comme barre de chargement, voici un capture d'écran :
La réponse de Ken avait l'air plutôt bonne, mais après avoir regardé quelques vidéos de la vraie télévision statique, j'ai eu quelques idées et voici ce que j'ai trouvé (deux versions):
Sommaire des modifications:
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;
}
J'ai réécrit votre code afin que chaque étape soit distincte afin que vous puissiez réutiliser les choses sans avoir à créer et recréer à chaque fois, réduit les appels en boucle et, espérons-le, suffisamment clair pour pouvoir le suivre en le lisant.
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);
}
Vous pouvez maintenant créer une boucle d'intervalle ou de temporisation qui continue de ré-invoquer la fonction générée.
window.setInterval(
generateNoise(.8, 200, 200),
100
);
Ou avec requestAnimationFrame
comme dans la réponse de Ken
var noise = generateNoise(.8, 200, 200);
(function loop() {
noise();
requestAnimationFrame(loop);
})();
Il se trouve que je viens d'écrire un script qui fait exactement cela, en récupérant les pixels d'une toile noire et en modifiant simplement les valeurs alpha aléatoires et en utilisant putImageData
Le résultat peut être trouvé sur 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);
}
Vous pouvez le faire comme ceci:
window.setInterval('generateNoise(.8)',50);
Le 2e arg 50
est un retard en millisecondes. En augmentant 50
le ralentira et diminuera dans le sens inverse.
cependant .. cela va sérieusement affecter les performances des pages Web. Si c'était moi, je ferais le rendu côté serveur et rendrais une poignée d'itérations de trame et de sortie sous forme de gif animé. Pas tout à fait la même chose que l'infinité aléatoire, mais ce serait une énorme amélioration des performances et la plupart des gens ne remarqueront même pas.
Le mien ne semble pas identique à la vraie télévision statique, mais il est néanmoins similaire. Je passe simplement en revue tous les pixels du canevas et je change les composants de couleur RVB de chaque pixel à une coordonnée aléatoire en une couleur aléatoire. La démo est disponible sur CodePen.
Le code est comme suit:
// 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);
}