Animazione canvas js: come fare rimbalzare delle palle per lo schermo
Tags: animazione, javascript, canvas
Published: 2021-03-02 08:13:16

---
title: Animazione canvas js: come fare rimbalzare delle palle per lo schermo
published: true
description: Come gestire una palla o più palle che rimbalzano da tutte le parti del canvas
tags:  animazione, ,javascript, js, canvas 
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8zn0391b7n97ett9dxjj.png
---
Ho già affrontato le animazioni in questo [articolo](https://dev.to/camizzilla/animazioni-con-il-canvas-di-javascript-come-cos-e-e-come-implementarlo-con-un-esempio-base-gh) dove ne ho parlato in maniera basica.

Come esempio ho fatto muovere un cerchio da un lato all'altro del canvas. Un animazione molto semplice e bruttina, ma che era giusto per lo scopo. 
Per questo in questo articolo impareremo qualcosa di più articolato (ma non troppo).

Nel primo esempio **faremo muovere una palla in 2d in giro per lo schermo e rimbalzerà nella direzione opposta toccato lo schermo.**

#INIZIALIZIAMO
 
Inizializiamo un elemento <canvas> grande 600x400

* **HTML**
``` html 
<canvas id="myCanvas"></canvas>
```

* **JS**
``` js
let canvas = document.getElementById('myCanvas');
canvas.width = 600;
canvas.height = 400;

let ctx = canvas.getContext('2d');
```

# FUNZIONE DI RENDERING

Creiamo un **funzione ricorsiva che servirà per il rendering** dell'animazione, quindi **avrà il compito di ripetere il codice al suo interno ciclicamente**.

**Tutto questo grazie al metodo _requestAnimationFrame()_** che chiameremo, una fuori dalla funzione per chimarla la prima volta e una all'interno per creare il loop.

``` js
let loop = () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  ctx.beginPath();
  ctx.arc(canvas.width / 2, canvas.height  / 2, 30, 0, 2 * Math.PI);
  ctx.fillStyle = 'red';
  ctx.fill();
  ctx.lineWidth = 3;
  ctx.strokeStyle = '#000';
  ctx.stroke();

  requestAnimationFrame(loop)
}

requestAnimationFrame(loop)
```
All'interno della funzione _loop()_ abbiamo:

1. la funzione che pulisce il canvas ad ogni ciclo
2. i metodi che disegnano un cerchio rosso con il contorno nero al centro (canvas.width / 2, canvas.height  / 2) del canvas
3. il metodo che richiama la funzione loop. Grazie a quest'ultimo metodo la funzione verrà richiamata circa 60 volte al secondo (60 fps).

Come si può vedere per disegnare un solo cerchio ci vogliono tante righe di codice, pensate se dovessimo disegnare più elementi, sarebbe un casino e non è riutilizzabile.

#CLASSE JS 
 
Per questo creeremo una classe _Circle_ che darà origine a quanti cerchi vogliamo e dove avremmo il totale controllo.

``` js
class Circle {
  constructor(){
    this.x = canvas.width / 2;
    this.y = canvas.height / 2;
    this.radius = 30;
    this.color = 'red';
  }
  
  draw(){
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
    ctx.fillStyle = this.color;
    ctx.fill();
    ctx.lineWidth = 3;
    ctx.strokeStyle = '#000';
    ctx.stroke();
  }
}


let loop = () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  requestAnimationFrame(loop)
}

let ball = new Circle();
requestAnimationFrame(loop)
```

Grazie alla classe _Circle_, con il comando _new_ abbiamo salvato un'istanza della classe nella variabile _ball_ .

Per ultimo abbiamo sostituito all'interno della funzione _loop_, il codice che disegnava un cerchio con il metodo _draw()_ della variabile _ball_.

Il risultato finale non è cambiato ma il codice è più ordinato e riutilizzabile.

#MUOVIAMO LA PALLA

Per far muovere la palla, aggiungiamo due proprietà nel costruttore della classe Circle che indicano di quanti pixel la nostra palla si dovrà spostare, una è per lo spostamento orizzontale e l'altra è per lo spostamento verticale.

``` js
class Circle {
  constructor(){
    this.x = canvas.width / 2;
    this.y = canvas.height  / 2;
    this.radius = 30;
    this.color = 'red';
    this.speedX = 3;
    this.speedY = 3;
  }
  ...
```

Queste due proprietà dovranno sommarsi ad ogni ciclo del loop, this.speedX con this.x e this.speedX con this.y. Quindi creiamo un nuovo metodo nella classe che compirà questo compito e nella funzione loop richiamiamo questo metodo che ho chimato _update()_.

``` js
class Circle {
  constructor(){
    ...
  }
  
  draw(){
    ...
  }

  update(){
    this.x += this.speedX;
    this.y += this.speedY;
  }


let loop = () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.update();
  ball.draw();
  requestAnimationFrame(loop)
}

...
```

La nostra funzione loop adesso cosa fa?
* cancella tutto quello che c'era disegnato nello schermo 
* aggiunge 3px alle due coordinate della palla.
* disegna la palla sulle nuove coordinate.
* ricomincia il giro facendo ripartire la funzione stessa.

Tutto questo è talmente veloce che si vede solo il movimento della palla.

#BORDI

Se provassimo il nostro script, la palla schizzerebbe fuori dai bordi per non tornare più.
Per questo dobbiamo recintare il nostro canvas in modo che appena la palla tocchi uno dei quattro bordi rimbalzi indietro.

Per fare questo dobbiamo controllare quando la circonferenza della palla tocca un bordo.
Dobbiamo tenere conto anche del raggio del cerchio oltre alle coordinate se no la nostra palla fuoriuscirà del canvas come nell'immagine sottostante.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sonw4p2k0pb6biv5xzlt.jpg)

Vediamo le 4 condizioni per la quale la palla dovrà rimbalzare:

* Margine sinistro: Se la x meno il raggio (x - r) è minore di 0
* Margine destro: Se la x più il raggio (x + r) è maggiore della lunghezza del canvas (canvas.width)
* Margine superiore: Se la y meno il raggio (y - r) è minore di 0
* Margine inferiore: Se la y più il raggio (y + r) è maggiore della altezza del canvas (canvas.height)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5z3vedsvyk27pispbyc2.jpg)

Traduciamo il tutto in un metodo della classe, _edgeIsTouched()_, richiamata poi nel _update()_

``` js
update(){
    this.edgeIsTouched();
    this.x += this.speedX;
    this.y += this.speedY;
  }
  
  edgeIsTouched(){
    if (this.x - this.radius < 0 || this.x + this.radius > canvas.width) {
      this.speedX = -this.speedX;
    } 
    if (this.y - this.radius < 0 || this.y + this.radius > canvas.height) {
      this.speedY = -this.speedY;
    }
```
Nel primo _if_ controlla se vengono superati i margini destra e sinistra, se così fosse, il valore dello speedX cambia di segno, se questo era positivo diventa negativo e viceversa, così la x andrà nel verso opposto a dove stava andando.

Uguale per quanto riguarda la y il controllo sui margine superiore e inferiore.

{% codepen  https://codepen.io/camizzilla/pen/ZEBaZGy %}

# 100 Palle per lo schermo

Modifichiamo il nostro programma per fare in modo che invece di una palla in giro per lo schermo ci siano 100 palle di dimensioni e colori diversi. 

Randomiziamo la dimensione, il colore e la direzione. 
Nel costruttore della classe, al posto dei valori del raggio, colore e delle due direzioni mettiamo la funzione _Math.random()_ che passa un valore casuale.

``` js
class Circle {
  constructor(){
    this.x = canvas.width / 2;
    this.y = canvas.height  / 2;
    this.radius = Math.random() * (30 - 10) + 10; 
    this.color = `hsl(${Math.random() * 360}, 100%, 50%)`;
    this.speedX = Math.random() * 5 - 2.5;
    this.speedY = Math.random() * 5 - 2.5;
  }
```

La funzione Math.random() ritorna un numero causale da 0 a 1, per questo dobbiamo dotarlo di un range massimo e minimo nel quale stare.

* `this.radius = Math.random() * (30 - 10) + 10;`
Il raggio della palla sarà compreso tra 10 e 30 px.
* `this.color = 'hsl(${Math.random() * 360}, 100%, 50%)'`;
Ho randomizzato la tonalità del colore con un range della scala dei colori che va da 0 a 360, mantendendo la saturazione e la luminosità fisse.
*  `this.speedX = Math.random() * 5 - 2.5;`
Per le direzioni ho un range che va da -2.5 a +2.5, così che mi possa tornare sia un numero positivo ( destra e in basso ), sia un numero negativo ( sinistra e in alto).

Il prossimo passo è creare una funzione _init() che crei, grazie ad un _for_, 100 instanze della classe Circle e le salvi in un array e che faccia partire il ciclo con _requestAnimationFrame_ .

Modifichiamo la funzione _loop() in modo che vengano disegnate tutte e aggiornate tutte le 100 palle contenute nell'array grazie al metodo _forEach_ e il gioco è fatto.

``` js
let balls = []

class Ball {
  ...
}

let init = () => {
  for (i = 0; i < 10; i++) {
    balls.push( new Ball())
  } 
  requestAnimationFrame(loop)
}

let loop = () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  balls.map( ball => {
    ball.draw();
    ball.update();
  })
  requestAnimationFrame(loop)
}

init()
```

{% codepen https://codepen.io/camizzilla/pen/mdOxWRz %}

# Conclusione

In questo tutorial abbiamo visto come animare una e più sfere che rimbalzano nel canvas, spero ti sia piacuto.

Se avete dei consigli, suggerimenti o critiche costruttive lasciatemi un commento qui sotto oppure contattatemi trammite i miei social.