2012-05-30 1 views
9

Ho provato entrambi su tela e nulla ha mostrato, anche se dubito che sia anche efficiente: /. Sto cercando di fare piovere che scende dallo schermo .. Chiedendosi qual è il modo più efficiente per farlo. Sono un principiante nell'animazione e apprezzerei davvero l'aiuto.html 5 canvas javascript pioggia animation (come implementare in modo efficiente e semplice!)

Sospetto che la creazione di un oggetto pioggia sia la cosa migliore, ciascuno con la qualità di scendere dallo schermo, quindi arrivare in alto e quindi un array con loro ... forse con valori x casuali con la larghezza della tela e valori y di 0 ma non so come implementarlo. Per favore aiuto!

   xofRain = 20; 
     startY = 0; 
     ctx.beginPath(); 
     ctx.moveTo(xofRain, startY); 
     ctx.lineTo(xofRain, startY + 20); 
     ctx.closePath(); 
     ctx.fillStyle = "black"; 
     ctx.fill(); 


    function rain(xofRain){ 

     startY = canvas.height(); 

     ctx.moveTo(xofRain, startY); 
     ctx.beginPath(); 
     ctx.lineTo(xofRain, startY + 3); 
     ctx.closePath(); 
     ctx.fillStyle = "blue"; 
     ctx.fill(); 
    } 
+0

Come dovrebbe essere questa "pioggia"? Solo strisce blu? (Non acqua rifrattiva, vero?) – Phrogz

+0

sì, esattamente! semplici strisce blu –

risposta

15

Qui viene la risposta, questa pioggia la neve viene creata usando pura HTML5 Canvas, la tecnica utilizzata per realizzare questa animazione viene chiamato "Double Buffer Animazione". Innanzitutto, è utile sapere qual è la tecnica di animazione Double Buffer.

Double Buffer Technique: tecnica avanzata per rendere l'animazione chiara e con meno sfarfallio. In questa tecnica viene utilizzato 2 Canvas, uno viene visualizzato sulla pagina Web per mostrare il risultato e il secondo viene utilizzato per creare schermate di animazione nel processo di backup.

Come questo aiuterà a pieno, supponiamo di dover creare un'animazione con un numero molto alto di mosse, come nel nostro esempio di Caduta di neve, ci sono un numero di scaglie che si muovono con la propria velocità, quindi tienili in movimento, abbiamo per cambiare la posizione di ogni fiocco e aggiornarlo sulla tela, questo è un processo abbastanza pesante da affrontare.

Così ora invece di aggiornare ogni Flake direttamente sul nostro canvas di pagina, creeremo un buffer Canvas, in cui tutte queste modifiche avvengono e catturiamo un'immagine da una tela di Buffer dopo 30ms e la mostriamo sulla nostra tela reale.

In questo modo la nostra animazione sarà chiara e senza sfarfallio. Quindi qui è un esempio vivo di esso.

http://aspspider.info/erishaan8/html5rain/

Ecco il codice di esso:

<!DOCTYPE html> 
 
    <html> 
 
    <head> 
 
    <meta charset=utf-8 /> 
 
    <title>HTML5 Rain</title> 
 
    <!--[if IE]> 
 
     <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> 
 
    <![endif]--> 
 
    <style> 
 
     article, aside, figure, footer, header, hgroup, 
 
     menu, nav, section { display: block; } 
 
    </style> 
 
    <script type="text/javascript"> 
 
     var canvas = null; 
 
     var context = null; 
 
     var bufferCanvas = null; 
 
     var bufferCanvasCtx = null; 
 
     var flakeArray = []; 
 
     var flakeTimer = null; 
 
     var maxFlakes = 200; // Here you may set max flackes to be created 
 
    
 
     function init() { 
 
      //Canvas on Page 
 
      canvas = document.getElementById('canvasRain'); 
 
      context = canvas.getContext("2d"); 
 
      //Buffer Canvas 
 
      bufferCanvas = document.createElement("canvas"); 
 
      bufferCanvasCtx = bufferCanvas.getContext("2d"); 
 
      bufferCanvasCtx.canvas.width = context.canvas.width; 
 
      bufferCanvasCtx.canvas.height = context.canvas.height; 
 
    
 
      
 
      flakeTimer = setInterval(addFlake, 200); 
 
    
 
      Draw(); 
 
    
 
      setInterval(animate, 30); 
 
       
 
     } 
 
     function animate() { 
 
      
 
      Update(); 
 
      Draw(); 
 
      
 
     } 
 
     function addFlake() { 
 
    
 
      flakeArray[flakeArray.length] = new Flake(); 
 
      if (flakeArray.length == maxFlakes) 
 
       clearInterval(flakeTimer); 
 
     } 
 
     function blank() { 
 
      bufferCanvasCtx.fillStyle = "rgba(0,0,0,0.8)"; 
 
      bufferCanvasCtx.fillRect(0, 0, bufferCanvasCtx.canvas.width, bufferCanvasCtx.canvas.height); 
 
      
 
     } 
 
     function Update() { 
 
      for (var i = 0; i < flakeArray.length; i++) { 
 
       if (flakeArray[i].y < context.canvas.height) { 
 
        flakeArray[i].y += flakeArray[i].speed; 
 
        if (flakeArray[i].y > context.canvas.height) 
 
         flakeArray[i].y = -5; 
 
        flakeArray[i].x += flakeArray[i].drift; 
 
        if (flakeArray[i].x > context.canvas.width) 
 
         flakeArray[i].x = 0; 
 
       } 
 
      } 
 
      
 
     } 
 
     function Flake() { 
 
      this.x = Math.round(Math.random() * context.canvas.width); 
 
      this.y = -10; 
 
      this.drift = Math.random(); 
 
      this.speed = Math.round(Math.random() * 5) + 1; 
 
      this.width = (Math.random() * 3) + 2; 
 
      this.height = this.width; 
 
     } 
 
     function Draw() { 
 
      context.save(); 
 
      
 
      blank(); 
 
    
 
      for (var i = 0; i < flakeArray.length; i++) { 
 
       bufferCanvasCtx.fillStyle = "white"; 
 
       bufferCanvasCtx.fillRect(flakeArray[i].x, flakeArray[i].y, flakeArray[i].width, flakeArray[i].height); 
 
      } 
 
    
 
      
 
      context.drawImage(bufferCanvas, 0, 0, bufferCanvas.width, bufferCanvas.height); 
 
      context.restore(); 
 
     } 
 
     
 
    </script> 
 
    </head> 
 
    <body onload="init()"> 
 
     <canvas id="canvasRain" width="800px" height="800px">Canvas Not Supported</canvas> 
 
    </body> 
 
    </html>

Anche se trovate questo aiuto pieno, accetta come risposta e lo compongono. o_O

Cheers !!!

1

Non sono sicuro di cosa sia "più efficiente". Se fossi in me, lo farei in WebGL, ma non è chiaro se sia efficiente o meno.

In entrambi i casi, proverei a usare una formula senza stato. Creare e aggiornare lo stato per ogni goccia di pioggia è discutibilmente lento.

const ctx = document.querySelector("canvas").getContext("2d"); 
 
const numRain = 200; 
 

 
function render(time) { 
 
    time *= 0.001; // convert to seconds 
 
    
 
    resizeCanvasToDisplaySize(ctx.canvas); 
 
    
 
    const width = ctx.canvas.width; 
 
    const height = ctx.canvas.height; 
 
    ctx.fillStyle = "black"; 
 
    ctx.fillRect(0, 0, width, height); 
 
    
 
    resetPseudoRandom(); 
 
    
 
    const speed = time * 500; 
 
    ctx.fillStyle = "#68F"; 
 
    for (let i = 0; i < numRain; ++i) { 
 
    const x = pseudoRandomInt(width); 
 
    const y = (pseudoRandomInt(height) + speed) % height; 
 
    ctx.fillRect(x, y, 3, 8); 
 
    } 
 
    
 
    requestAnimationFrame(render); 
 
} 
 

 
requestAnimationFrame(render); 
 

 
let randomSeed_ = 0; 
 
const RANDOM_RANGE_ = Math.pow(2, 32); 
 

 
function pseudoRandom() { 
 
    return (randomSeed_ = 
 
      (134775813 * randomSeed_ + 1) % 
 
      RANDOM_RANGE_)/RANDOM_RANGE_; 
 
}; 
 

 
function resetPseudoRandom() { 
 
    randomSeed_ = 0; 
 
}; 
 

 
function pseudoRandomInt(n) { 
 
    return pseudoRandom() * n | 0; 
 
} 
 

 
function resizeCanvasToDisplaySize(canvas) { 
 
    const width = canvas.clientWidth; 
 
    const height = canvas.clientHeight; 
 
    if (canvas.width !== width || canvas.height !== height) { 
 
    canvas.width = width; 
 
    canvas.height = height; 
 
    } 
 
}
body { margin: 0; } 
 
canvas { width: 100vw; height: 100vh; display: block; }
<canvas></canvas>

noti che ho potuto usare ctx.moveTo(x, y); ctx.lineTo(x, y + 8); per ciascuna linea e quindi al termine del ciclo chiamato ctx.stroke(). Non l'ho fatto perché suppongo che sarebbe meno efficiente rispetto all'utilizzo di ctx.fillRect. Perché la tela possa disegnare linee, in realtà deve allocare un percorso dinamico (si chiama ctx.beginPath). Quindi deve registrare tutte le linee che aggiungi. Quindi deve espandere quelle linee in vertici di vario genere per rasterizzare le linee. Puoi fondamentalmente see the various algorithms it uses here.Viceversa nessuno di questi deve accadere con ctx.fillRect. Nessuna allocazione deve accadere (non dicendo che non accadono, solo dicendo che non devono). Il canvas può semplicemente usare un singolo quad pre-allocato e disegnarlo sulla GPU passando la matrice corretta per disegnare qualunque rettangolo tu chieda. Ovviamente potrebbero essere più overhead chiamando lo ctx.fillRect 200 volte anziché ctx.moveTo, ctx.lineTo 200s + ctx.stroke una volta, ma questo dipende dal browser.

La pioggia sopra può o non può essere un effetto pioggia abbastanza buono. Non era il mio punto di postare davvero. Il punto è l'efficienza. Praticamente tutti i giochi che hanno una sorta di effetto pioggia fanno una sorta di formula senza stato per la loro pioggia. Una formula diversa genererebbe piogge diverse o meno ripetitive. Il punto è che è senza stato.