Introduzione a p5.js
:play_button: provare a eseguire il codice dopo la modifica
01. Funzioni setup() e draw()
- funzione
setup()
- cos’è una funzione
- funzioni chiamate automaticamente da p5.js
- nel setup():
print("chiamata setup()")
:play_button: - funzione
draw()
- nel draw():
print("chiamata draw()")
:play_button: - differenze setup() e draw()
02. Colori (e background())
- nel setup() (senza draw()):
background(0)
:play_button: - grigi RGB:
background(128)
:play_button: - colori RGB:
background(180, 204, 230)
:play_button: - grigi RGB semitrasparenti:
background(128, 128)
:play_button: - colori RGB semitrasparenti:
background(180, 204, 230, 128)
:play_button: - uso della modalità cromatica HSL
- inizio setup():
colorMode(HSL)
:play_button: - grigio HSL:
background(50)
:play_button: - colore HSL:
background(210, 50, 80)
:play_button: - trasparenza HSL:
background(210, 50, 80, 0.5)
:play_button:
Modifiche complessive
function setup() {
colorMode(HSL)
background(210, 50, 80)
}
03. Dimensioni del canvas
- nel setup() (prima di colorMode()):
createCanvas(400,300)
:play_button: - dimensioni della finestra di visualizzazione:
windowWidth
ewindowHeight
- adattamento alla finestra:
createCanvas(windowWidth, windowHeight)
:play_button: - intercettazione ridimensionamento finestra:
windowResized()
- ridimensionamento del canvas:
resizeCanvas()
- canvas adattato al ridimensionamento della finestra: :play_button:
function windowResized() {
resizeCanvas(windowWidth, windowHeight)
}
- verifica corretto funzionamento (nel windowResized() dopo resizeCanvas()):
background(210, 50, 80)
:play_button:
Modifiche complessive
function setup() {
createCanvas(windowWidth, windowHeight)
colorMode(HSL)
background(210, 50, 80)
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight)
}
04. Disegno di figure geometriche
- disegno di un cerchio:
circle()
- nel draw():
circle(0, 0, 200)
:play_button: - spostamento dall’origine (in alto a sinistra):
circle(50, 50, 200)
:play_button: - cambiamento dimensione:
circle(50, 50, 100)
:play_button: - colore di riempimento:
fill()
- riempimento azzurro chiaro (prima di circle()):
fill(200, 55, 85)
:play_button: - colore del contorno:
stroke()
- contorno bianco (prima di circle()):
stroke(100)
:play_button: - spessore del contorno:
strokeWeight()
- spessore di 10 pixel (prima di circle()):
strokeWeight(10)
:play_button: - forzatura spessore di default (1 pixel):
strokeWeight(1)
:play_button:
Modifiche complessive
...
function draw() {
fill(200, 55, 85)
stroke(100)
strokeWeight(1)
circle(50, 50, 100)
}
...
05. Variabili e parametrizzazione
- memorizzare e utilizzare valori variabili
- definire e inizializzare variabili globali (utilizzabili in ogni parte del codice):
let x = 50
let y = 50
let diameter = 100
function setup() {
...
}
- leggere i valori memorizzati
- usare le variabili nell’istruzione circle() (nel draw()):
circle(x, y, diameter)
:play_button: - aggiungere altro
circle(x, y, diameter)
- impostare dimensioni proporzionali a cerchio precedente (1/8 del diametro):
circle(x, y, diameter / 8)
:play_button: - ottenere spostamenti proporzionali: :play_button:
let offset = diameter / 5 // variabile locale utilizzabile solo
// nel blocco ({...}) in cui è definita
circle(x-offset, y-offset, diameter/8)
- moltiplicazione anziché divisione, per intuire meglio percentuale (20% anziché 1/5):
let offset = diameter * 0.2
:play_button: - disattivazione contorno (prima del 2° circle()):
noStroke()
:play_button: - riempimento bianco (prima del 2° circle()):
fill(100)
:play_button:
Modifiche complessive
let x = 50
let y = 50
let diameter = 100
function setup() {
...
}
function draw() {
fill(200, 55, 85)
stroke(100)
strokeWeight(1)
circle(x, y, diameter)
fill(100);
noStroke();
let offset = diameter * 0.2
circle(x-offset, y-offset, diameter/8)
}
06. Animazione
- aggiunta variabile (globale) di incremento della posizione:
let speed = 5
- incremento variabile x (alla fine del draw()):
x = x + speed
:play_button: - incremento variabile y (anziché x):
y = y + speed
:play_button: - decremento variabile y (anziché x):
y = y - speed
:play_button: - animazione anziché accumulazione progressiva di disegni
- spostamento di
background(210, 50, 80)
dal setup() all’inizio del draw() :play_button: - partenza dal basso della bolla (modifica inizializzazione):
let y = height
:play_button:
Modifiche complessive
...
let y = height
...
let speed = 5
function setup() {
...
}
function draw() {
background(210, 50, 80)
...
y = y - speed
}
07. Casualità
- la funzione
random()
- rendere casuale il valore di
x
, fra margine sinistro (0) e margine destro (width
) - sostituire
let x = 50
conlet x = random(width)
:play_button: - rendere casuale il diametro, fra 50 pixel e 120
- sostituire
let diameter = 100
conlet diameter = random(50, 120)
:play_button: - rendere casuale la velocità, da 1 pixel al fotogramma a 5 pixel al fotogramma
- sostituire
let speed = 5
conlet speed = random(1, 5)
:play_button: - dipendenza valori da parametro casuale (
diameter
) - spostare inizializzazione di
diameter
a prima di quella dix
- variabile
y
dipendente dal raggio (diameter/2
):let y = height + diameter/2
:play_button: - velocità proporzionale al diametro:
let speed = diameter / 30
:play_button:
Modifiche complessive
let diameter = random(50, 120)
let x = random(width)
let y = height + diameter/2
let speed = diameter / 30
...
08. Classi e proprietà
- definire in un’unica entità le variabili e le istruzioni di oggetti simili
- differenza fra classe e istanze
- definire la classe della bolla:
class Bubble {
}
... // variabili globali
function setup() {
...
}
function draw() {
...
}
...
- definire le proprietà della classe e i valori iniziali:
class Bubble {
constructor() {
this.diameter = random(50, 120)
this.x = random(width)
this.y = height + diameter/2
this.speed = diameter / 30
}
}
- eliminare le variabili globali
x
,y
,diameter
espeed
- aggiungere variabile globale per istanza della classe Bubble:
let bubble
- creare istanza (nel setup()):
bubble = new Bubble()
- usare istanza al posto delle variabili (nel draw()):
x
->bubble.x
y
->bubble.y
diameter
->bubble.diameter
speed
->bubble.speed
:play_button:
Modifiche complessive
class Bubble {
constructor() {
this.diameter = random(50, 120)
this.x = random(width)
this.y = height + diameter/2
this.speed = diameter / 30
}
}
let bubble
function setup() {
...
bubble = new Bubble()
}
function draw() {
...
circle(bubble.x, bubble.y, bubble.diameter)
...
let offset = bubble.diameter * 0.2
circle(bubble.x-offset, bubble.y-offset, bubble.diameter/8)
}
09. Metodi delle classi
- aggiunta metodo display() (per disegno)
class Bubble {
...
display() {
}
}
- spostamento istruzioni del draw() (tranne background()) nel metodo display
- sostituzione di
bubble
conthis
(riferimento a proprietà della classe):bubble.x
->this.x
bubble.y
->this.y
bubble.diameter
->this.diameter
bubble.speed
->this.speed
- uso del metodo display (nel draw() dopo background()):
bubble.display()
:play_button:
Modifiche complessive
class Bubble {
...
display() {
fill(200, 55, 85)
stroke(255)
strokeWeight(1)
circle(this.x, this.y, this.diameter)
fill(100)
noStroke()
let offset = this.diameter * 0.2
circle(this.x-offset, this.y-offset, this.diameter/8)
}
}
...
function draw() {
background(210, 50, 80)
bubble.display()
}
10. Array di istanze
- sostituzione istanza singola (
bubble
) con array di istanze - sostituzione di
let bubble
conlet bubbles = []
(array vuoto) - creazione di istanze e memorizzazione nell’array (nel setup())
- sostituzione di
bubble = new Bubble()
conbubbles[0] = new Bubble()
- aggiunta istanza nel secondo elemento:
bubbles[1] = new Bubble()
- uso delle istanze (nel draw())
- sostituzione di
bubble.display()
conbubbles[0].display()
:play_button: - aggiunta chiamata metodo seconda istanza:
bubbles[1].display()
:play_button:
Modifiche complessive
...
let bubbles = []
function setup() {
...
bubbles[0] = new Bubble()
bubbles[1] = new Bubble()
}
function draw() {
background(210, 50, 80)
bubbles[0].display()
bubbles[1].display()
}
11. Iterazione di istruzioni (for)
- ripetizione della creazione e dell’uso delle istanze: la struttura di controllo
for
- definire variabile globale con numero di elementi dell’array:
let totalBubbles = 50
- creare e memorizzare istanze (nel setup())
- al posto di
bubbles[0] = new Bubble()
ebubbles[1] = new Bubble()
inserire: \
for (let i = 0; i < totalBubbles; i++) {
let b = new Bubble()
bubbles.push(b)
}
- uso di tutte le istanze create (nel draw())
- al posto di
bubbles[0].display()
ebubbles[0].display()
inserire: :play_button: \
for (let i = 0; i < bubbles.length; i++) {
let b = bubbles[i]
b.display()
}
Modifiche complessive
let bubbles = []
let totalBubbles = 50
function setup() {
...
for (let i = 0; i < totalBubbles; i++) {
let b = new Bubble()
bubbles.push(b)
}
}
function draw() {
background(210, 50, 80)
for (let i = 0; i < bubbles.length; i++) {
let b = bubbles[i]
b.display()
}
}
12. Istruzioni condizionali
- far eseguire o meno un’istruzione: la struttura di controllo
if
- reimpostare y quando la bolla è fuori dal canvas
- nel metodo display() aggiungere: :play_button:
if (this.y < 0) {
this.y = height + this.diameter/2;
}
- attesa sparizione completa: sostituire
this.y < 0
conthis.y < this.diameter/2
:play_button:
Modifiche complessive
class Bubble {
...
display() {
...
if (this.y < this.diameter/2) {
this.y = height + this.diameter/2;
}
}
}
13. Interattività
- eventi e variabili di sistema (gestiti da p5.js)
- intercettare la pressione del tasto del mouse:
mousePressed()
- aggiunta di un’istanza alla pressione del tasto del mouse (fuori da ogni altra funzione): :play_button: \
function mousePressed() {
bubbles.push( new Bubble() )
}
- posizionamento istanza sotto il cursore del mouse: le variabili
mouseX
emouseY
- al posto di
bubbles.push(new Bubble())
: :play_button: \
let b = new Bubble()
b.x = mouseX
b.y = mouseY
bubbles.push( b )
- modifica cursore del mouse (nel setup()):
cursor(HAND)
:play_button:
Modifiche complessive
function setup() {
...
cursor(HAND)
}
function draw() {
...
}
function mousePressed() {
let b = new Bubble()
b.x = mouseX
b.y = mouseY
bubbles.push( b )
}
14. Aggiunta proprietà (life)
- usare una proprietà per limitare la durata dell’istanza (ad esempio:
life
) - aggiungere (nel costruttore della classe):
this.life = random(100,1000)
- ridurre la “vita” dell’istanza a ogni fotogramma (nel metodo display()):
this.life = this.life - 1
- evitare che scenda sotto lo 0 (“morte” dell’istanza):
if (this.life > 0) {
this.life = this.life - 1;
}
- prova temporanea di funzionamento (come 4° parametro del 1° fill()) del display():
fill(200, 55, 85, this.life/1000)
- separazione visualizzazione (sole istruzioni di disegno) da aggiornamento (sola gestione valori delle proprietà)
- aggiunta metodo update(): \
class Bubble {
...
update() {
}
}
- trasferimento codici di aggiornamento delle proprietà da display():
class Bubble {
...
update() {
this.y = this.y - this.speed;
if (this.y < -this.diameter/2) {
this.y = height+this.diameter/2;
}
if (this.life > 0) {
this.life = this.life - 1;
}
}
}
- sostituzione
for
del draw() per iterare le istanze dall’ultima alla prima:for (let i=bubbles.length-1; i >= 0; i=i-1) {
- display() eseguito solo se istanza “viva”: \
let b = bubbles[i];
b.update();
if (b.life > 0) {
b.display();
}
- eliminazione istanza in caso contrario: \
if (b.life > 0) {
...
} else {
bubbles.splice(i, 1)
}
- far apparire le nuove istanze sopra le altre
- nel mousePressed(), sostituire
bubbles.push(b)
conbubbles.unshift(b)
Modifiche complessive
class Bubble {
constructor() {
...
this.life = random( 100, 1000 );
}
display() {
fill(200, 55, 85);
stroke(255);
strokeWeight(1);
circle(this.x, this.y, this.diameter);
fill(100);
noStroke();
let offset = this.diameter * 0.2;
circle(this.x - offset, this.y - offset, this.diameter / 8);
}
update() {
this.y = this.y - this.speed;
if (this.y < -this.diameter/2) {
this.y = height+this.diameter/2;
}
if (this.life > 0) {
this.life = this.life - 1;
}
}
}
...
function draw() {
...
for ( let i=bubbles.length-1; i >= 0; i=i-1 ) {
let b = bubbles[i];
b.update()
if (b.life > 0) {
b.display()
} else {
bubbles.splice(i, 1);
}
}
}
function mousePressed() {
...
bubbles.unshift( b );
}
Riferimenti
Codice finale
\
class Bubble {
constructor() {
this.diameter = random(50, 120);
this.x = random(width);
this.y = height+this.diameter/2;
this.speed = this.diameter / 30;
this.life = random( 100, 1000 );
}
display() {
fill(200, 55, 85);
stroke(255);
strokeWeight(1);
circle(this.x, this.y, this.diameter);
fill(100);
noStroke();
let offset = this.diameter * 0.2;
circle(this.x - offset, this.y - offset, this.diameter / 8);
}
update() {
this.y = this.y - this.speed;
if (this.y < -this.diameter/2) {
this.y = height+this.diameter/2;
}
if (this.life > 0) {
this.life = this.life - 1;
}
}
}
let bubbles = [];
let totalBubbles = 100;
function setup() {
createCanvas(windowWidth, windowHeight);
colorMode(HSL);
for ( let i=0; i < totalBubbles; i=i+1 ) {
bubbles[i] = new Bubble();
}
}
function draw() {
background(210, 50, 80);
for ( let i=bubbles.length-1; i >= 0; i=i-1 ) {
let b = bubbles[i];
b.update()
if (b.life > 0) {
b.display()
} else {
bubbles.splice(i, 1);
}
}
}
function mousePressed() {
let b = new Bubble();
b.x = mouseX;
b.y = mouseY;
bubbles.unshift( b );
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
background(210, 50, 80);
}
Link utili
Codice completo nel web editor:
Full immersion in p5.js
Esempio di partenza:
How to Program Interactive Art With p5.js.
Introduzione ai concetti base di p5.js:
Introduzione a p5.js
Guida di riferimento alle variabili e alle istruzioni di p5.js:
P5.js Reference