Temporary layers / Multiple canvases using p5


#1

Hey - I’m an experienced javascript developer and have done a bit with plain javascript and html5 canvas.

What I’m doing is something similar to the spirograph example on the examples page - the difference being that I want each of the dots to draw a permanent trace - as well as having the temporary circles on top.

The non-p5 way of doing this, looks a bit like this:

let t = 0; 
drawAnimationFrame() {
   //getMyObjectsToDraw is an expensive function to call, so we only want to run it once
   //It generates a list of temporary objects, and a list of permenant objects, to draw for a given t
   const {tempObjs, permObjs} = getMyObjectsToDraw(t++);  

   //We clear the temporary context each frame
   tempContext.clearRect(0, 0 WIDTH, HEIGHT); 

   //These objects have a function that tells them how to draw themselve on the context
   for (let obj of   tempObjs) {
      obj.draw(tempContext); 
   }             
   for (let obj of permObjs) {
      obj.draw(permContext); 
   }

   // Next frame
   window.requestAnimationFrame(this.drawAnimationFrame);
}

window.requestAnimationFrame(this.drawAnimationFrame);

That is - we’re using one requestAnimationFrame loop to draw on multiple canvases.

I can’t see how I would do this with P5 - given that the draw loop seems to refer to a single canvas. If I were using a two canvas solution - I’d have to either call my expensive getMyObjectsToDraw method twice, or do something like where, if I’m using instance mode, the first instance then passes the list of objects to the second one to draw… which sounds like it would have all sorts of synchronisation issues.

Alternatively, I’m wondering if there’s an established way of doing a ‘multiple layers on the same canvas’ in p5?


#2

p5.Graphics is a good way to create multiple layers. Here’s an example. You could draw the trace to a graphics layer and render that each time through draw.


#3

Fantastic, thanks.

This is what my solution ends up looking like (instance mode):

p.setup = function () {
    p.createCanvas(200, 200);
    p.background(0);
    p.temp = p.createGraphics(200, 200);
    p.perm = p.createGraphics(200, 200);
};

//I call this function externally to update how the thing draws.
p.doUpdate = function (controlsPackage, statePackage, algoFn) {
    p.t = 0;
    p.controlsPackage = controlsPackage;
    p.statePackage = statePackage;
    p._algoFn = algoFn;
}

p.draw = function () {
    //Get the draw objects
    const { temp, perm } = p._algoFn(p.t, p.controlsPackage, p.statePackage, p);
    
    //Draw the permanents on one layer
    perm.forEach(v => v(p.perm));

    //Clear the temp layer and draw on it. 
    p.temp.clear();
    temp.forEach(v => v(p.temp));

    //Clear the actual p5 canvas, and then draw both the temp and perm layers on it. 
    p.clear();
    p.image(p.temp, 0, 0);
    p.image(p.perm, 0, 0);

    p.t++;
};