Search code examples
javascriptp5.js

How to group DOM elements and their methods in p5.js?


I had made a menu for a game in p5.js and I wanted the menu to be simple but well presented and very interactive. Also, I wanted to have a small piece of code. I have achieved the first conditions but I still think my code is very big.

I encourage you to please change/edit/delete my code and write the same idea in a better way

let modes = [];
var mode1, mode2, mode3, mode4;

function setup() {
  createCanvas(400, 500);
  mode1 = createP("Mode 1");
  mode2 = createP("Mode 2");
  mode3 = createP("Mode 3");
  mode4 = createP("Mode 4");
  mode1.class("mode");
  mode2.class("mode");
  mode3.class("mode");
  mode4.class("mode");
  modes = selectAll(".mode");
  for (var i = 0; i < modes.length; i++) {
    modes[i].style("font-size", "50px");
  }
  mode1.position(40, 115);
  mode2.position(215, 115);
  mode3.position(40, 300);
  mode4.position(215, 300);
}

function draw() {
  background("#befecd");
  noFill();
  strokeWeight(8);
  rect(20, 100, 360, 360);
  line(20, 280, 380, 280);
  line(200, 100, 200, 460);
  fill(0);
  textSize(64);
  text("MENU", 100, 70);
  mode1.mouseOver(function () {
    mode1.html("Mode 1<br>description");
    mode1.style("font-size", "35px");
  });
  mode2.mouseOver(function () {
    mode2.html("Mode 2<br>description");
    mode2.style("font-size", "35px");
  });
  mode3.mouseOver(function () {
    mode3.html("Mode 3<br>description");
    mode3.style("font-size", "35px");
  });
  mode4.mouseOver(function () {
    mode4.html("Mode 4<br>description");
    mode4.style("font-size", "35px");
  });
  mode1.mouseOut(function () {
    mode1.html("Mode 1");
    mode1.style("font-size", "35px");
  });
  mode2.mouseOut(function () {
    mode2.html("Mode 2");
    mode2.style("font-size", "50px");
  });
  mode3.mouseOut(function () {
    mode3.html("Mode 3");
    mode3.style("font-size", "50px");
  });
  mode4.mouseOut(function () {
    mode4.html("Mode 4");
    mode4.style("font-size", "50px");
  });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>

This are some ideas I have thought about:

  • Making that

    elements on the HTML page

  • Keep the different properties on variable variables and have only one mouseOver() / mouseOut() function for all the DOM elements
  • As you can see, I have gotten all elements on another variable, but I haven’t used that. Shall I?
  • Change to another programming language. I don’t feel very confortable with p5.js sometimes when I do games or those types of things. I ask you for other programming languages (not the best or personal opinion, I only want to know other options)

Solution

  • The solution to the thing1, thing2, thing3... thingN in pretty much all languages is arrays and iteration (usually loops in imperative languages).

    You can factor out the variable parts of each repeated chunk of logic and generalize to structure that represents the raw data, then loop over it and build your elements from those variables.

    const modeData = [
      {description: "foo foo foo", position: [40, 115]},
      {description: "bar bar bar", position: [215, 115]},
      {description: "baz baz baz", position: [40, 300]},
      {description: "quux quux", position: [215, 300]},
    ];
    const modes = [];
    
    function setup() {
      createCanvas(400, 500);
      modeData.forEach(({description, position: [x, y]}, i) => {
        const p = createP(`Mode ${i + 1}`);
        modes.push(p);
        p.class("mode");
        p.style("font-size", "45px");
        p.position(x, y);
        p.mouseOver(() => {
          p.html(`Mode ${i + 1}<br>${description}`);
          p.style("font-size", "35px");
        });
        p.mouseOut(() => {
          p.html(`Mode ${i + 1}`);
          p.style("font-size", "50px");
        });
      });
    }
    
    function draw() {
      background("#befecd");
      noFill();
      strokeWeight(8);
      rect(20, 100, 360, 360);
      line(20, 280, 380, 280);
      line(200, 100, 200, 460);
      fill(0);
      textSize(64);
      text("MENU", 100, 70);
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js"></script>

    As you can see, the mouse over/out listeners shouldn't be re-added on every frame in draw, only one time in setup.

    As a UI/UX/app design aside, it's a bit odd to combine canvas drawing and DOM elements like this, especially when resizing is involved. You can see some odd flashing between mouseover/out at certain mouse positions when the description text is small. I'd likely use CSS-styled DOM <div> elements rather than canvas lines to create the boxes. If the text content is larger, the problem disappears, but the point stands since there are other layout issues that can occur. Your use case may be an exception, so this is just a rule of thumb.

    Note also that you start at 45px, then return to 50px after mouseout. Maybe those two numbers should be the same.