Si pasaste por el curso de Introducción a JS, entonces hiciste algunos botones sencillos en los desafíos de lógica Tu primer botón y Botón más inteligente. En caso que lo hayas olvidado, vamos a repetir cómo hacer un botón sencillo.
Primero, ¿cuáles son las características mínimas de un botón?
  1. Una figura en el lienzo (típicamente un rectángulo)
  2. Incluye una etiqueta o ícono que describe lo que hará
  3. Responde al clic del usuario sobre él (pero no en otros lugares)
Podemos lograr #1 y #2 muy fácilmente:
fill(0, 234, 255);
rect(100, 100, 150, 50, 5);
fill(0, 0, 0);
textSize(19);
text("Botón inútil", 110, 133);
Para lograr #3, necesitamos definir una función mouseClicked que será llamada cuando el usuario haga clic, y dentro de ella tenemos que verificar que mouseX y mouseY están dentro del rectángulo del botón. En el caso del botón anterior, va de x=100 hasta x=250, y de y=100 hasta y=150, como se ilustra a continuación:
Podemos verificar esas coordenadas con && y cuatro condiciones:
mouseClicked = function() {
    if (mouseX >= 100 && mouseX <= 250 &&
        mouseY >= 100 && mouseY <= 150) {
        println("Todavía bastante inútil");    
    }
};
Trata de hacer clic en el botón y afuera para verificar que funciona:
Definitivamente funciona, pero también me preocupa. Me preocupa que no sea un código muy reutilizable. ¿Cuánto trabajo tendré que hacer si quiero cambiar la posición del botón? (¡Inténtalo arriba!). Veo un montón de números "incrustados" en el código, como las coordenadas en la función mouseClicked, e inmediatamente empiezo a preguntarme si no hay una manera más limpia.
Para empezar, hagamos variables la posición y el tamaño, para que podamos cambiarlos en un solo lugar y que siga funcionando el clic en el botón. Agregué btnX, btnY, btnWidth y btnHeight al programa que se muestra a continuación. Intenta cambiar los valores y hacer clic en el botón:
Bueno, eso está mejor. Pero aún así, ¿cuánto trabajo tendré que hacer si quiero añadir otro botón? ¿Tengo que copiar y pegar todo eso y hacer btn2X, btn2Y? Uf, eso no suena nada divertido. Esto nos da una buena motivación para escribir una función que se encargará de hacer todo lo que sea igual para los botones, y usar parámetros para encargarse de lo que sea diferente. Podríamos escribirla así, si convertimos las variables en parámetros:
var drawButton = function(btnX, btnY, btnWidth, btnHeight) {
    background(255, 255, 255);
    fill(0, 234, 255);
    rect(btnX, btnY, btnWidth, btnHeight, 5);
    fill(0, 0, 0);
    textSize(19);
    textAlign(LEFT, TOP);
    text("Botón inútil", btnX+10, btnY+btnHeight/4);
};
Luego la llamaríamos así:
drawButton(100, 100, 150, 50);
Oh no, pero ¿qué pasa con nuestro código mouseClicked? ¿Puedes ver cuál sería el problema con eso?
mouseClicked = function() {
    if (mouseX >= btnX && mouseX <= (btnX+btnWidth) &&
        mouseY >= btnY && mouseY <= (btnY+btnHeight)) {
        println("Todavía bastante inútil");    
    }
};
Si tuviéramos todo este código junto, nos saldría un error diciendo que "btnX no está definido" ("btnX is not defined"), ¡y estaría en lo correcto! Convertimos btnX en un parámetro de la función, lo que significa que ya no es una variable global. Eso es fantástico para la reutilización de la función drawButton, pero ahora la función mouseClicked no tiene manera de saber qué coordenadas verifiicar.
Así que tenemos que inventar una buena manera de pasarle la información a drawButton y hacer que esa información esté disponible para mouseClicked. Puedo pensar en algunas opciones:
  1. Reintroducir las variables globales para la posición y el tamaño (btnX, btnY, btnWidth, btnHeight)
  2. Introducir un arreglo global que almacene todos los parámetros (var btn1 = [...];)
  3. Introducir un objeto global que almacene los parámetros (var btn1 = {..})
  4. Usar principios orientados a objetos para definir el botón y almacenar las propiedades (var btn1 = new Button(...))
¿Cuál elegir? Bueno, no me gusta la primera porque tendríamos que agregar muchas variables globales, y tengo una alergia a las variables globales. No me encanta la segunda técnica porque es difícil de leer código que toma datos basados en los índices de arreglos. Me gusta la tercera técnica porque introduce solamente una variable global y producirá código más legible. También me gusta la cuarta técnica, utilizar principios orientados a objetos para crear tipos de objeto Button genéricos, pero dejemos eso para más adelante.
Podemos crear nuestro objeto global btn1 así:
var btn1 = {
    x: 100,
    y: 100,
    width: 150,
    height: 50
};
Y cambiar la función drawButton para que acepte un único objeto del cual obtenga las propiedades:
var drawButton = function(btn) {
    background(255, 255, 255);
    fill(0, 234, 255);
    rect(btn.x, btn.y, btn.width, btn.height, 5);
    fill(0, 0, 0);
    textSize(19);
    textAlign(LEFT, TOP);
    text("Botón inutil", btn.x+10, btn.y+btn.height/4);
};
La función mouseClicked revisará las propiedades de la variable global:
mouseClicked = function() {
    if (mouseX >= btn1.x && mouseX <= (btn1.x+btn1.width) &&
        mouseY >= btn1.y && mouseY <= (btn1.y+btn1.height))     {
        println("Todavía bastante inútil");    
    }
};
¡Pruébalo a continuación! Como antes, intenta cambiar diferentes parámetros del botón y ver que todo áun funcione:
El punto de eso era permitirnos agregar fácilmente más botones, la prueba máxima de la reutilización. ¿Podemos hacerlo? Ba Bum BUM.
Empezaremos con una nueva variable global, btn2, desplazada en la dirección y a partir del primer botón:
var btn2 = {
    x: 100,
    y: 200,
    width: 150,
    height: 50
};
Luego dibujaremos ese botón:
drawButton(btn2);
Eso tendrá éxito en dibujar 2 botones en el lienzo, pero solo el primero responderá a los clics. Podemos hacer que el segundo responda al duplicar la lógica e intercambiar btn2 por btn1, así:
mouseClicked = function() {
    if (mouseX >= btn1.x && mouseX <= (btn1.x+btn1.width) &&
        mouseY >= btn1.y && mouseY <= (btn1.y+btn1.height))     {
        println("Still pretty useless");    
    }

    if (mouseX >= btn2.x && mouseX <= (btn2.x+btn2.width) &&
        mouseY >= btn2.y && mouseY <= (btn2.y+btn2.height))     {
        println("2nd one still quite useless!");    
    }
};
¿Pero todo ese código que se repite no hace que se te arrugue la la nariz? Hagamos una función isMouseInside que sepa cómo revisar cualquier objeto botón y regrese true si el ratón estaba dentro de él:
var isMouseInside = function(btn) {
    return (mouseX >= btn.x &&
            mouseX <= (btn.x+btn.width) &&
            mouseY >= btn.y &&
            mouseY <= (btn.y+btn.height));
};
Y ahora podemos usar esa función dentro de mouseClicked para reducir considerablemente la cantidad de código repetitivo:
mouseClicked = function() {
    if (isMouseInside(btn1))     {
        println("Todavía bastante inútil");    
    } else if (isMouseInside(btn2))     {
        println("¡El 2o es aún bastante inútil!");    
    }
};
¡Y ahí lo tenemos! Usamos funciones para dibujar varios botones, e hicimos que sea relativamente sencillo agregar nuevos botones. Pruébalo a continuación:
Podríamos continuar (hacer arreglos de todos los botones en un programa, permitir personalizar la etiqueta y el color de un botón) pero espero que esto te haya dado una buena base sobre cómo crear botones sencillos usando funciones. A continuación, veremos cómo crear botones usando principios orientados a objetos.
Cargando