Nous voyons ici, dans ce cinquième article de la série p5.js mes "briques" de connaissance, comment constituer un array (tableau) d’images que nous pourrons manipuler. Ces images sont chargées soit par l’action d’un utilisateur (glissé – déposé d’images sur le canevas), soit par le chargement d’images aléatoires via des informations JSON de l’API d’unsplash.
Je présente deux solutions ici de chargement de fichiers. La première permet de glisser des fichiers images sur le canevas. La deuxième va chercher des images aléatoires sur Unsplash. Dans les deux cas on constitue un array (tableau) d’images.
Constituer un array (tableau) d’images
Un tableau d’image est un tableau classique, sauf que son contenu est formé d’images. Dans le script que vous pouvez voir en action ici, on constitue un array de nbImg
images. Cet array est constitué dans la fonction preload, en mode asynchrone. Une fois que toutes les images sont chargées, et seulement à ce moment, la fonction setup() se lance, crée le canevas puis redimensionne les images. Et enfin la fonction draw() affiche l’une des images, prise au hasard. Si l’utilisateur appuie sur la touche a (ou A), une autre image s’affiche.
/****************************************************************
https://editor.p5js.org/Anne-Laure/sketches/cdNa-nKUH
charger un array d'images
****************************************************************/
var img = [];
var nbImg = 12 ;
function preload() {
// noter que les dimensions sont un tout petit peu différentes pour avoir des url différentes
// cf https://github.com/unsplash/unsplash-source-js/issues/9
for( let i = 0 ; i < nbImg ; i++ ) {
img[i] = loadImage( 'https://source.unsplash.com/random/1024x6' + nf(i,2,0) );
}
}
function setup() {
createCanvas(windowWidth, windowHeight);
for( let i = 0 ; i < nbImg ; i++ ) {
let newW = min(img[i].width, 0.9*width) ;
img[i].resize( newW, 0);
}
}
function draw() {
background(240) ;
let num = int(random(img.length));
image( img[num], 10, 10);
text("taper a ou A pour changer d'image", 10, img[num].height + 30 ) ;
text("taper s ou S pour sauvegarder le canevas", 10, img[num].height + 60 ) ;
noLoop() ;
}
// Export when key is pressed
function keyReleased() {
if (key == 'a' || key == 'A') {
redraw();
}
if (key == 's' || key == 'S') {
save();
}
}
Par glissé-déposé de fichiers image sur le canevas
J’explique ce que je comprends de la solution de Takawo, un codeur japonais. J’ai présenté rapidement cette solution dans mon article sur la brique précédente : « p5.js Brique 4 : charger des images en mode asynchrone« . Ici je vais expliquer un peu plus ce que je comprends du code du sketch.
Le travail de Takawo a été mis en ligne sur OpenProcessing ici. On peut également le voir sur mon dépôt GitHub (et ici il s’éxécute). Ce sketch reçoit (en une ou plusieurs fois) des fichiers par simple glisser-poser sur le canevas puis les assemble. Si on superpose des images avec des transparences comme ici, il y a un effet intéressant. Ca donne par exemple cette image :
Ce script utilise les fonctionnalités p5.js de la fonction drop(). C’est une fonction qui sert à définir ce qu’il faut faire lorsq’un fichier est déposé sur le canevas et chargé. Le script exécute les actions suivantes :
- Dans
setup()
: donner au canevas le nom « c ». Appliquer à c la fonctiondrop()
de telle sorte que la fonctiongotFile
, s’exécute à chaque fois qu’un fichier est déposé sur le canevas c. Danssetup()
on déclenche aussi la fonctioninit()
. - La fonction
gotFile()
vérifie, pour chaque fichier, que c’est une image. Si oui, elle ajoute cette image dans l’arrayimgs
et elle exécute la fonctiondrawImage()
sur cette image. - La fonction
init()
est exécutée au setup et à chaque fois que quelqu’un appuie sur la touche r ou R.init()
initialise un tableau (array) nomméimgs
et demande que la fonctiondraw()
soit exécutée de nouveau. C’est une sorte de remise à zéro. - la fonction
drawImage()
assure la mise en scène de toutes les images les unes avec les autres. Je n’en ai pas regardé le fonctionnement mais j’aime bien le résultat. - La fonction
draw()
est exécuté au démarrage et à chaque fois que init() est déclenché. Le canevas est vidé, son fond est gris et un texte indique que l’on peut y placer plusieurs fichiers d’images. mousePressed()
redessine en appelantdrawImage()
sur les fichiers existants. Comme il y a une partie aléatoire dansdrawImage()
, le canevas change.- Et enfin
keyPressed()
définit ce qui est fait lorsque les touches s ou S d’une part, r ou R d’autre part sont pressées.
C’est un beau script, avec une grande simplicité. Je suis admirative.
Chargement via du JSON et l’API d’Unsplash
Le script est visible sur https://editor.p5js.org/Anne-Laure/sketches/3981uPnuE.
Dans ce script il y a une différence essentielle avec le premier script ci-dessus, dans lequel on constituait un array d’images : on constitue d’abord un array d’URL et ensuite on charge les images correspondantes. Et pour constituer l’Array d’url, il faut récupérer des données au format JSON et les lire (étape 1). Une fois qu’on a lu les données (et obligatoirement uniquement lorsque c’est fait), on doit charger les images correspondant aux url trouvées (étape 2). Une fois qu’on a chargé les images (et obligatoirement uniquement lorsque c’est fait), on doit afficher les images et le texte des url (étape 3).
Ca n’est pas très compliqué dans l’absolu sauf qu’il y a deux fois « (et obligatoirement uniquement lorsque c’est fait) » entre deux étapes successives. Et c’est là que je me suis bien arrachée les cheveux pour obtenir le résultat qui suit (très moche je suis d’accord mais j’en suis hyper fière cependant) :
Pour voir le code complet du sketch : c’est ici sur GitHub ou ici sur l’éditeur p5.js en ligne. On peut également voir l’exécution ici sur une page GitHub.
Récupérer des données au format JSON (étape 1)
Je précise que j’ai mis plusieurs jours à comprendre des explications qui paraissaient certainement claires pour leurs auteurs. Alors je n’ai pas la prétention d’être limpide. Je vous recommande de vous munir de patience et de temps !
Ici je veux récupérer des images aléatoires en provenance du site Unsplash.com. Unsplash.com dispose d’une API (Interface de programmation ou Application Programming Interface) . Une API c’est un moyen de faire communiquer des programmes entre eux. Unsplash a créé une documentation sur son API mais ce n’est pas simple au départ à comprendre. Dans le script présenté j’utilise seulement la possibilité de charger un nombre nbImg
d’images choisies aléatoirement. D’autres options existent, telles que la sélection de photos contenant une couleur donnée ou correspondant à des mots clés précisés.
Utilisation de loadJSON
Dans la fonction preload() ci-dessous on utilise loadJSON
pour charger en mode asynchrone des données au format JSON. la variable client_id contient une valeur que j’ai trouvé dans un exemple d’url sur internet. Chacun peut s’inscrire gratuitement sur Unsplash.com et obtenir cet identifiant client. On notera que la fonction loadJSON utilise une fonction callback nommée processJson()
.
function preload() {
let url = 'https://api.unsplash.com/photos/random/?client_id=';
url = url + client_id + '&count=' + nbImg ;
print("url",url) ;
loadJSON(url, processJson );
}
La structure du JSON retourné
Pour savoir à quoi ressemble le format JSON, le plus simple est de taper une url construite selon les règles de l’API concernée et de regarder ce qui revient dans le navigateur. Ensuite on sélectionne ce qui est revenu (du texte pas très facile à lire), on le copie avec CTRL C et on va le coller dans un formatteur de JSON tel que celui-ci. Et là miracle le texte illisible se structure et on commence à le comprendre :
Ici je vois que les données JSON qui m’intéressent sont dans la structure sous « urls » puis « full ».
Quelques ressources sur le format JSON et son utilisation en p5.js
- JSON selon wikipedia ;
- les explications de p5js.org sur la fonction loadJSON() ;
- un exemple d’utilisation de loadJSON() ;
- Vidéos de Daniel Shiffman, en anglais :
- 10.1: Introduction to Data and APIs in JavaScript – p5.js Tutorial
- 10.2: What is JSON? Part I – p5.js Tutorial
- 10.3: What is JSON? Part II – p5.js Tutorial
- 10.4: Loading JSON data from a URL (Asynchronous Callbacks!) – p5.js Tutorial
Le traitement du JSON retourné
avec processJson()
j’ai mis ce qu’il fallait pour lire les informations qui m’intéressent. data est le nom arbitraire que j’ai donné aux données qui sont retournées par la requête initiale. Dans data on cherche à récupérer, pour chacun des nbImg éléments, le sous élément « full » de « urls ». Ce sous élément est contenu dans data[i].urls.full
. Ensuite croppedUrl[i] est construit selon les règles de l’API Unsplash pour obtenir une image découpée de hauteur 600 et largeur 1500.
Charger les images (étape 2)
La fonctionprocessJson()
s’éxécute de manière asynchrone, uniquement lorsque la fonction loadJSON a récupéré toutes les données JSON. Donc lorsque processJson() démarre, on est certains d’avoir des url définies. loadImage() peut se dérouler correctement.
function processJson( data ) {
for (let i = 0; i < nbImg; i++) {
imgUrl[i] = data[i].urls.full ;
// découpe l'image de manière aléatoire pour qu'elle fasse 1500 de large
croppedUrl[i] = imgUrl[i] + '&crop=entropy&h=600&w=1500&fit=crop' ;
img[i] = loadImage( croppedUrl[i] );
}
}
Et lorsque la callback fonction processJson( data )
est totalement terminée (les images sont toutes chargées), alors seulement elle retourne à son origine loadJSON()
et lui « dit » que ça y est on peut continuer. La fonction preload()
peut enfin se terminer et la fonction setup()
démarre.
afficher les images et le texte des url (étape 3)
il n’y a rien de spécial dans le code. La fonction setup() retaille toutes les images à la bonne taille. La fonction draw() les affiche et positionne le texte des url sur le canevas.
function setup() {
createCanvas(windowWidth, windowHeight);
for (let i = 0; i < nbImg; i++) {
let newW = width / 4 -( 10 * 4 ) ;
img[i].resize( newW, 0) ;
}
noLoop();
}
function draw() {
background(200);
let pos = createVector(10,10) ;
for (let i = 0; i < nbImg; i++) {
image(img[i], pos.x, pos.y ) ;
pos.x = pos.x + img[i].width + 10 ;
if ( (pos.x + img[i].width) > width ) {
pos.x = 0 ;
pos.y = pos.y + img[i].height + 10 ;
}
}
pos.y = pos.y + 20 ;
text( "full URLs", 20, pos.y ) ;
for (let i = 0; i < nbImg; i++) {
pos.x = 20 ;
pos.y = pos.y + 15 ;
text( imgUrl[i], pos.x, pos.y ) ;
}
pos.y = pos.y +20 ;
text( "cropped URLs", 20, pos.y ) ;
for (let i = 0; i < nbImg; i++) {
pos.x = 20 ;
pos.y = pos.y + 15 ;
text( croppedUrl[i], pos.x, pos.y ) ;
}
}
Et maintenant ?
Et voilà nous avons une cinquième brique ! Les autres briques sont et seront publiées dans cette même série, p5.js mes "briques" de connaissance.