Hi, I’m having trouble getting the text from an API to display on my canvas, I can get it outside of it but I can’t find the way to put it in. I have the API key in my code and I know the variable names are kinda weird. My end goal is tho display the content of 5 news stories with the provider, time, title, description and image in the canvas and then making an image of each one (I think with createImage()) so I can apply somo kind of algorithm to them. I’ve been trying to do this for a couple of days but can’t find the way to do it.
let url = 'https://api.jornalia.net/api/v1/articles?apiKey=?&providers=Clarin%2CTN%2CPagina12%2CLaNacion%2CInfobae&categories=POLITICA%2CECONOMIA%2CSOCIEDAD'
let h4, h5, h1, P, img;
function setup(){
loadJSON(url, gotData, 'json');
createCanvas(windowWidth, windowHeight);
background(150);
}
function gotData(data){
for ( let i = 0; i <= 4; i++){
h4 = data.articles[i].provider.name;
h5 = data.articles[i].publishedAt;
h1 = data.articles[i].title;
P = data.articles[i].description;
img = data.articles[i].imageUrl;
}
}
function draw(){
let news = createElement('h4', h4);
text(news,20,20);
}
There are a few issues going on, but first and foremost is the usage of the text() function. The text function in p5.js expects a string for its first parameter, not an p5.Element which is what createElement() returns. If you want to format text that you render to the canvas with text() you need to use methods like textSize() / textStyle() / textFont().
Other issues include:
You should avoid calling createElement and the like in draw() unless you disable looping with noLoop() unlike text drawn to the canvas, HTML elements created this way are persistent so this would be a huge memory leak.
Your for loop keeps overwriting all your variables, so you’ll only really capture the data from the 5th article (the one with index 4).
Calls to draw that happen before the data is loaded will fail because h4 (and the other variables containing article data) have not yet been initialized. Once a call to draw fails, p5.js stops calling it.
Here’s a working example:
let url = 'https://www.paulwheeler.us/files/example.json';
let h4 = '', h5, h1, P, img;
function setup() {
createCanvas(windowWidth, windowHeight);
loadJSON(url, gotData, 'json');
}
function gotData(data){
print('gotData');
console.log(data);
for (let i = 0; i < data.articles.length; i++) {
// This is going to only store the last article
// information in these variables
h4 = data.articles[i].provider.name;
h5 = data.articles[i].publishedAt;
h1 = data.articles[i].title;
P = data.articles[i].description;
img = data.articles[i].imageUrl;
}
// This creates an HTML element that is separate from your canvas
let news = createElement('h4', h4);
// By default, an h4 element will be a block element added after your canvas.
// The position function changes the element to be positioned absolutely
news.position(0,0);
}
function draw() {
background(255);
// The p5.js text function can only take a string.
// It cannot take an HTML element. if you want HTML-like
// formatting and layout you either need to use HTML or
// implement it yourself with p5.js drawing commands.
text(h4, width / 2, height / 2);
}
Thank you very much! I kept working on it and could put the text inside the canvas but now I’m havieng other problems, I wonder if you’d know how to fix them. First I can’t seem to find a way to put the image below the texI tried changing the img variable, I thought by putting the ’ ’ inside it it would grab the url of the image and load it in in the loadImage function but it didn’t work. The other problem is I don’t know how to display those 4 different sets of texts and images on draw. I tried putting another for loop but couldn’t make it work.
let url = 'https://api.jornalia.net/api/v1/articles?apiKey=?&providers=Clarin%2CTN%2CPagina12%2CLaNacion%2CInfobae&categories=POLITICA%2CECONOMIA%2CSOCIEDAD'
let prov = '', time= '', title = '', desc = '', img = '';
function setup(){
loadJSON(url, gotData, 'json');
createCanvas(windowWidth, windowHeight);
}
function gotData(data){
for ( let i = 0; i < 4; i++){
prov= data.articles[i].provider.name;
time = data.articles[i].publishedAt;
title = data.articles[i].title;
desc = data.articles[i].description;
img = data.articles[i].imageUrl;
}
}
function draw(){
background(150);
for( j = 0; j < 4; j++){
textSize(16);
text(prov,10,20);
textSize(12);
text(time,10,40);
textSize(30);
text(title,10,70);
textSize(16);
text(desc,10,100);
loadImage(img);
image(img,10,120);
}
}
Regarding the second issue: displaying multiple articles, the problem is that you are trying to store multiple values (one for each article) in individual variables (i.e. prov, time, title) etc. What you need is arrays which you add items to (or better yet a single array to which you add an object containing the article data). Then you can use a for loop in your draw function to draw each article. However, you also don’t want to draw the articles over top of one another. So you need to determine how you want to arrange the articles and then draw them in different places. The easiest way to accomplish the latter (drawing in different places) is with push()/translate()/pop() (recommended viewing: Transformations (Translate, Rotate, Push/Pop) - Part 1 - Additional Topics Tutorial #9.1 · The Coding Train). Here’s a functional example:
let url = 'https://www.paulwheeler.us/files/example2.json';
let articles = [];
function setup() {
createCanvas(windowWidth, windowHeight);
loadJSON(url, gotData, 'json');
}
function gotData(data) {
print('gotData');
console.log(data);
for (let i = 0; i < data.articles.length; i++) {
let content = {
prov: data.articles[i].provider.name,
time: data.articles[i].publishedAt,
title: data.articles[i].title,
desc: data.articles[i].description,
imgSrc: data.articles[i].imageUrl,
imgLoaded: false
};
content.img = loadImage(
imgSrc,
() => {
content.imgLoaded = true;
}
);
articles.push(content);
}
}
function draw() {
background(255);
// Simple hack: assume each article fits in a 200px x 200px sqaure
let articlesPerRow = floor(width / 200);
for (let i = 0; i < articles.length; i++) {
// Save the current drawing position
push();
// determine layout position
let y = floor(i / articlesPerRow) * 200;
let x = (i % articlesPerRow) * 200;
// translate to the top left corner of the current article's grid spot
translate(x, y);
textSize(16);
text(articles[i].prov, 10, 20);
textSize(12);
text(articles[i].time, 10, 40);
textSize(30);
text(articles[i].title, 10, 70);
textSize(16);
text(articles[i].desc, 10, 100);
if (articles[i].imgLoaded) {
image(articles[i].img, 10, 120);
} else {
text('Loading...', 10, 120);
}
// Revert back to the previous drawing position
pop();
}
}
I really can’t thank you enough, I’ve been trying a lot of things this past couple of days but couldn’t do it, thanks for all the advice! Recently I’ve been watching a lot of videos by the coding train too, I’ll give that one a watch!