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:
elements on the HTML page
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.