Interactie

Deze site is grotendeels een vertaling van een tutorial van Allison Parrish en is vertaald en bewerkt door Marijn van der Meer voor het gebruik bij Informatica lessen op het IJburg College

In dit hoofdstuk ga je leren hoe je in p5.js sketches kan maken die reageren op input van de gebruiker.

Ingebouwde p5.js variabelen

Je hebt gezien hoe je variabelen in je sketch moet gebruiken om ze flexibeler te maken: je kan de waarde van de variabele steeds opnieuw gebruiken. p5.js heeft een aantal ingebouwde variabelen die je zo in je sketch kan gebruiken zonder ze te moeten aanmaken. Deze variabelen geven informatie over dingen die normaal buiten je sketch zouden liggen.

Je hebt al een handige ingebouwde variabele geleerd,frameCount, in een vorig hoofdstuk.

Breedte en hoogte

De width en height variabelen bevatten altijd de waarde die gelijk zijn aan de breedte en hoogte van je sketch (in pixels). Dit is eigenlijk altijd gelijk aan de waarde die je aan de parameters van de createCanvas() functie geeft.

De width en height variabelen zijn handig omdat ze je in staat stellen om sketches te maken die rekening houden met de grootte van het canvas. Hier zijn bijvoorbeeld twee versies van dezelfde sketch, beiden hebben alleen een andere grootte, maar beiden laten de cirkel op exact een derde van het canvas zien:

Deze verhouding blijft zelfs bestaan als we het canvas rechthoekig maken in plaats van vierkant:

Muispositie

p5.js heeft twee heel speciale variabelen, mouseX en mouseY, die de X en Y coordinaten van de muis cursor bevatten van het huidige frame. Als je bedenkt dat de code in draw() continue draait en heel snel: tot zestig keer per seconde. Een stukje code van p5.js dat deel uitmaakt van de library roept continue deze functie aan en iedere keer dat p5.js dat doet wordt de waarde van de mouseX en de mouseY variabelen geupdated met de huidige positie van de cursor op het scherm. De twee variabelen samen maken het heel makkelijk om je sketch te laten reageren met input van de gebruiker in de vorm van muisbewegingen.

Hier is een kort en simpel voorbeeld dat een cirkel tekent op de postitie van de muisaanwijzer:

Natuurlijk kan je de mouseX en mouseY niet alleen gebruiken voor de positie van de vormen die je tekent, maar ook voor andere elementen. Hier is een sketch die de mouseX waarde gebruikt om de breedte van de lijn en de mouseY waarde om de grootte van de ellips te bepalen:

Een klassieke voorbeeld sketch van p5.js is om de background() functie in draw() weg te laten, terwijl je de muispositie variabelen gebruikt. Nu heb je eigenlijk de basis van een tekenprogramma:

We kunnen een wat onvoorspelbaarder tekenprogramma maken door wat veranderingen in tijd in te voeren en wat andere kleine aanpassingen:

Gedrag sturen met if

Wanneer je te maken hebt met input van een gebruiker zou je vaak willen dat je sketch verschillende soorten reacties geeft. Onder de ene omstandigheid zou je willen dat je sketch op de ene manier reageert; en onder een tweede omstandigheid wil je dat je sketch anders reageert op de gebruiker.

Stel je bijvoorbeeld eens voor dat je een sketch wil maken waarbij er alleen een rechthoek verschijnt wanneer de muiscursor zich op de onderste helft van de sketch bevindt. Dus als de muispositie groter is dan de helft van de hoogte dan moet de rechthoek te zien zijn (en anders niets).

Om deze taak te volbrengen moeten we de sketch sturen. We moeten een mogelijkheid hebben om te vragen "controleer of iets zo is; als dat zo is doe dan dit." Javascript (en iedere andere programmeertaal) heeft speciale syntax voor juist dit doel: het if statement.

Hier is de sketch die precies de bovenstaande situatie uitvoert.

Het nieuwe gedeelte van deze code zijn deze drie regels:

if (mouseY > height/2) {
    rect(100, 100, 200, 200);
}

Dit is een voorbeeld van een if statement. Schematisch gezien ziet een if statement er zo uit:

if (expr) {
    code-die-uitgevoerd-moet-worden
}

… waarbij expr een relationale expressie (uitleg volgt hieronder) is en code-die-uitgevoerd-moet-worden een of meer Javascript statements zijn (zoals functie aanroepen, for loops, of zelfs andere if statements). Als de relationale expressie waar is, dan wordt de code-die-uitgevoerd-moet-worden uitgevoerd. Anders worden de statements niet uitgevoerd en wordt de betreffende code overgeslagen.

Relationele expressies

Relationale expressies lijken op die andere expressies waar we al naar hebben gekeken maar zij hebben de volgende structuur:

expr operator expr

Dus een operator met aan beide kanten een expressie. Het verschil tussen relationele operatoren en andere operator is dat de relationele operatoren niet een ander getal als antwoord krijgen maar een van de twee bijzondere waarden: true (waar) of false (onwaar). Relationele expressies worden gebruikt om vast te stellen hoe twee waarden relateren met elkaar. Meer specifiek: of een waarde groter is dan de andere of dat beide waarden gelijk zijn.

De meest gebruikte relationele operatoren zijn:

operator betekenis
> groter dan
< kleiner dan
== gelijk aan
>= groter of gelijk aan
<= kleiner dan of gelijk aan
!= niet gelijk aan

Probeer de volgende statement eens uit in de p5.js webeditor zie wat de antwoorden zijn in het consolevenster:

console.log(15 > 10);
console.log((3 * 30) < (2 * 40));
console.log((3 + 5) * 2 == (10 +  6));

Je zou dit moeten zien in het consolevenster:

true
false
true

Meerdere ifs

Je kan meerdere if statements in de draw() sectie van je code plaatsen. (In feite, kan je if statements overal plaatsen waar je zou willen, zelfs in for loops of andere if statements!). Hier een voorbeeldsketch dat een rechthoek tekent als de muis in de de onderste helft is en een cirkel als de muis op de rechterhelft van de sketch is:

Je kan ook een if statement in een andere if plaatsen. Hier dezelfde sketch, maar nu wordt de cirkel alleen getoond wanneer de muis aan de rechterkant en op de onderste helft van het scherm is:

Muiskliks

p5.js heeft een speciale ingebouwde variabele genaamd mouseIsPressed waar de waarde true is als de gebruiker de muisknop ingedrukt houdt en false als dat niet zo is. Je kan dit gebruiken om je sketch verschillende dingen te laten doen afhankelijk van of de gebruiker de muisknop ingedrukt houdt of niet.

Hier een voorbeeld waarbij alleen een cirkel te zien als de muisknop ingedrukt wordt:

Anders…

Stel dat je de volgende opdracht krijgt: maakt een sketch waarbij een cirkel wordt getoond aan de bovenkant van de sketch als de muis bovenin de sketch is en een cirkel onderin de sketch in de andere gevallen.

Je eerste poging zal er waarschijnlijk zoiets uit zien:

Maar dat is niet helemaal goed! De onderste cirkel wordt nu altijd getoond en niet alleen wanneer de relationele conditie in de if statement daarboven false is. Dit voorbeeld laat zien dat iedere code die na een if statement’s sluitingsaccolade wordt uitgevoerd, ongeacht of de if statement slaagt.

Je zou dit kunnen oplossen door twee verschillende if statements te gebruiken, een om te checken of de muispositie in de bovenste helft is en een andere om te checken of de muispositie op de onderste helft is:

OEFENING: Waarom gebruikt de tweede if statement >= als operator, in plaats van de >?

Dit werkt, maar het kan veel makkelijker. Het blijkt dat heel gebruikelijk is bij het programmeren dat je een bepaald gedrag wilt bij een bepaalde conditie en een ander gedrag wanneer niet aan deze conditie voldaan wordt. Om je tijd te besparen in het typen van een tweede if statement dat eigenlijk het omgekeerde is van het eerste statement heeft Javascript een else statement die je na iedere if statement kan plaatsen.

Het ziet er zo uit:

if (expr) {
  wat-code
}
else {
  wat-andere-code
}

… waarbij expr een expressie dat als antwoord true of false heeft (net als een relationele expressie of een variabele als mouseIsPressed). Als deze expressie als antwoord true heeft zal de code uit wat-code uitgevoerd worden. Anders zal wat-andere-code uitgevoerd worden.

Dus we kunnen het bovenstaande voorbeeld wat herschrijven in nog kortere code:

En hier een voorbeeld dat een rechthoek tekent als de muis wordt ingedrukt en anders een lijn:

Een vreemde eend

Het komt voor dat je in een for loop bij een keer tijdens die loop eigenlijk net iets anders wil laten gebeuren. Een if statement is het perfecte gereedschap in dit geval! Je kan een if statement in een for loop stoppen om te zien of de loop variabele (meestal de i) een bepaalde waarde heeft en juist op dat moment iets anders laten gebeuren, zoals hier:

Meer dan twee: else if

We leven niet in een het-een-of-het-ander wereld en is een het hebben van alleen een if en een else niet genoeg. Neem bijvoorbeeld eens het if/else voorbeeld van eerder, waar een cirkel bovenaan het scherm wordt getoond als de muis over de bovenste helft gaat en een cirkel aan de onderkant als de muis over de onderste helft gaat. Als je bijvoorbeeld een zelfde soort sketch wil maar dan met drie cirkels met een scherm dat in drie delen wordt gedeeld: als de muis op de bovenste derde deel gaat moet daar een cirkel verschijnen; over het midden dan een cirkel daar en op het onderste deel een cirkel daar.

In dit voorbeeld hebben we drie absoluut andere condities die we moeten testen. En de logica van de code zit er ongeveer zo uit:

  • Is de muisaanwijzer boven het bovenste derde deel van het scherm?
  • Als dat zo is: teken daar een cirkel.
  • Als dat niet zo is, vraag: Is de muisaanwijzer boven het middelste derde deel van het scherm?
  • Als dat zo is: teken daar een cirkel.
  • Als dat niet zo is: moet de muisaanwijzer over het onderste derde deel gaan, dus teken de cirkel daar.

Het makkelijkst om deze logica te beschrijven in Javascript is om de else if clausule te gebruiken. Qua syntax ziet deze structuur er zo uit:

if (test) {
    statements
}
else if (test) {
    statements
}
else {
    statements
}

Hier een code die de boven beschreven situatie uitvoerd met het gebruik van een else if:

Iedere if statement kan een ongelimiteerde hoeveelheid else if clausules bevatten. Een else clausule is zelfs helemaal niet altijd nodig bij een else if.

Relationele expressies combineren met && en ||

Ik heb hierboven al uitgelegd dat relationele expressies met < en > altijd bijzondere waarden als uitkomst hebben, namelijk booleans. Een booleaanse waarde is true (waar) of false (onwaar).

Er zijn nog twee speciale operatoren die gebruikt worden met booleaanse waarden: && en ||. De && heet de “logische EN” en de || is de “logische OF.” Wanneer expressies met deze operatoren worden geevalueerd (het antwoord van deze vergelijking) is de waarde van het resultaat zelf ook een boolean. Je kan met deze operatoren relationele expressies combineren om nog diepere relationele expressies te formeren.

De && en || operatoren werken net als iedere andere Javascript operator die je tot nu toe hebt gezien: ze verwachten een expressie links en een expressie rechts van de operator om te vergelijken. De uitkomst van de && operator is alleen true (waar) las zowel de expressie aan de linkerkant als aan de rechterkant true zijn, en de uitkomst is anders false (onwaar). De || operator daarintegen evalueert alleen tot true als een van beide expressies aan de linker- of rechterkant waar is, en alleen tot false als beide expressies niet waar zijn.

Je kan deze operatoren, net als de eerder getestte operatoren, in de console.log() testen in de webeditor. Dus, bijvoorbeeld:

console.log((6 > 5) && (7 > 6))

… zal true laten zien in het consolevenster, en

console.log((6 > 5) && (10 < 9))

… zal tot false leiden. En als we de || operator gebruiken:

console.log((6 > 5) || (10 < 9))

… laat true zien (omdat alleen een van de expressies waar hoeft te zijn voor de <|| operator om te slagen), en leidt

console.log((4 > 5) || (10 < 9))

… tot false (omdat beide expressies links en rechts van de || false) zijn.

Hier is een voorbeeld dat de && operator gebruikt om te zien of de muispositie in een bepaald gebied is. Net als het voorbeeld in het vorige stuk, alleen wordt nu een cirkel in het midden getoond als de muispositie groter is dan een derde van boven en lager is dan een derde van onderen. Anders wordt er boven en onder een cirkel getekend.

Hier wordt een cirkel getekend als of de muisknop wordt ingedrukt of de muiscursor zich bevind op de onderste helft van het scherm:

OEFENING: Maak een sketch dat een rechthoek tekent, die blauw is wanneer de muiscursor over de rechthoek gaat, en anders rood.

Samenwerken: alles binnenboord houden

Men gebruikt if statements in combinatie met relationele expressies om dingen binnenboord te houden. Als je een waarde hebt die met de tijd verandert wil je kunnen checken of die waarde een bepaalde grens is overschreden en als dat zo is moet er iets met die waarde gebeuren om deze te resetten of om van richting te laten veranderen.

Het klassieke voorbeeld is de stuiterende bal: teken een ellips die van links naar rechts beweegt. En wanneer het de rand van de sketch raakt moet de ellips van rechts naar links bewegen tot het weer de rand raakt, enzovoort:

Je kan dit bereiken door de xspeed variabele te vermenigvuldigen met -1, dus eigenlijk de richting omkeren, wanneer de X-positie “buitenboord raakt":

OEFENING: Pas de bovenstaande sketch zo aan dat het de snelheid van de ellips in zowel de X- als de Y-dimensie bijhoudt. Voeg een tweede if statement toe dat de ellips binnenboord houdt ook in de Y-richting (net zoals dat gebeurt met de bestaande if statement voor de X-as).