主要内容
按钮函数
首先,按钮的基本特征是什么?
- 画布上的一个形状(通常是一个矩形)
- 包括一个标签或图标,用来描述它的作用
- 响应用户的点击(但不在其他地方)
我们可以很容易地实现特征 #1 和 #2:
fill(0, 234, 255);
rect(100, 100, 150, 50, 5);
fill(0, 0, 0);
textSize(19);
text("无用的按钮", 110, 133);
想要实现特征 #3, 我们需要定义一个
mouseClicked
函数,当用户单击时就调用这个函数,并且在函数中,我们需要检查 mouseX
和 mouseY
是否在按钮的边框内。对于上面的按钮, 它的范围是从 x=100 到 x=250,从 y=100 到 y=150,如下图所示:我们可以用
&&
连接四个条件来检查这些坐标:mouseClicked = function() {
if (mouseX >= 100 && mouseX <= 250 &&
mouseY >= 100 && mouseY <= 150) {
println("仍然没什么用");
}
};
尝试点击它,并松开鼠标,来验证它是否有效:
它确实有效,但也让我担心。我担心它不具有很好的可重用性。如果我想改变按钮的位置,我需要做什么?(在上面试试看!)我在代码中看到了很多“硬编码”的数字——比如
mouseClicked
函数中的坐标,我就立即开始怀疑是否有更简单的方法。首先,让我们创建位置和大小的变量,这样我们就可以在一个地方更改它们,并且实现单击按钮后的效果。我在下面的程序中添加了
btnX
, btnY
, btnWidth
和 btnHeight
。尝试改变它们的值,然后单击按钮:这样就更好了。但是,如果我想添加一个额外的按钮,我还需要做什么呢?我需要把上面所有的代码复制粘贴,然后再创建
btn2X
, btn2Y
?呃,听起来一点都不好玩。这给了我们动力去写一个函数来处理对于所有按钮都相同的事情,并使用参数来处理不同的事情。我们可以把变量改为参数:var drawButton = function(btnX, btnY, btnWidth, btnHeight) {
fill(0, 234, 255);
rect(btnX, btnY, btnWidth, btnHeight, 5);
fill(0, 0, 0);
textSize(19);
textAlign(LEFT, TOP);
text("无用的按钮", btnX+10, btnY+btnHeight/4);
};
然后可以这样调用它:
drawButton(100, 100, 150, 50);
但是,我们
mouseClicked
的代码呢?你看出来问题出在哪里了吗?mouseClicked = function() {
if (mouseX >= btnX && mouseX <= (btnX+btnWidth) &&
mouseY >= btnY && mouseY <= (btnY+btnHeight)) {
println("仍然没什么用");
}
};
如果我们把所有这些代码放在一起,检错小帮手会提示一个错误,“btnX没有定义”——他是对的!我们将
btnX
转换为一个函数的参数,这表示它不再是一个全局变量。这提升了 drawButton
函数的可重用性,但是现在 mouseClicked
函数无法得到要检查的坐标。因此,我们需要找到一种合适的方式来将信息传递到
drawButton
,并且将这些信息提供给 mouseClicked
。我想到了一些方法:- 重新引入位置和大小的全局变量(
btnX, btnY, btnWidth, btnHeight
) - 引入一个全局数组来存储所有参数(
var btn1 = [...];
) - 引入一个全局对象来存储参数(
var btn1 = {..}
) - 使用面向对象原则定义按钮并存储属性(
var btn1 = new Button(...))
选择哪一个?我不喜欢第一种,因为我们需要添加太多的全局变量,我对全局变量过敏。我不喜欢第二种方法,因为读取基于数组索引抓取数据的代码可读性很差。我喜欢第三种方法,因为它只引入了一个全局变量,代码也具有更好的可读性。我也喜欢第四种方法,使用面向对象的原则来创建通用的
Button
对象类型,但是让我们等一会儿再实现这种方法。我们可以像这样创建全局
btn1
对象:var btn1 = {
x: 100,
y: 100,
width: 150,
height: 50
};
然后改变
drawButton
函数以接受一个对象,并从该对象中抓取属性:var drawButton = function(btn) {
fill(0, 234, 255);
rect(btn.x, btn.y, btn.width, btn.height, 5);
fill(0, 0, 0);
textSize(19);
textAlign(LEFT, TOP);
text("无用的按钮", btn.x+10, btn.y+btn.height/4);
};
mouseClicked
函数将检查全局变量的属性:mouseClicked = function() {
if (mouseX >= btn1.x && mouseX <= (btn1.x+btn1.width) &&
mouseY >= btn1.y && mouseY <= (btn1.y+btn1.height)) {
println("仍然没什么用");
}
};
在下面试一试!像之前一样,尝试改变按钮的不同参数,看看一切是否仍然有效:
这样做可以让我们能够轻松地添加更多的按钮,这是最终的可重用性测试。我们能做到吗?哒哒哒。
我们先引入新的全局变量
btn2
,从第一个按钮向 y 方向偏移:var btn2 = {
x: 100,
y: 200,
width: 150,
height: 50
};
然后, 我们将画出该按钮:
drawButton(btn2);
这将成功地在画布上绘制2个按钮,但只有第一个按钮将响应点击。我们可以通过相同的逻辑将
btn1
改为 btn2
,来使第二个按钮也响应, 如下所示:mouseClicked = function() {
if (mouseX >= btn1.x && mouseX <= (btn1.x+btn1.width) &&
mouseY >= btn1.y && mouseY <= (btn1.y+btn1.height)) {
println("仍然没什么用");
}
if (mouseX >= btn2.x && mouseX <= (btn2.x+btn2.width) &&
mouseY >= btn2.y && mouseY <= (btn2.y+btn2.height)) {
println("第二个也没什么用!");
}
};
但是,这些重复的代码难道不会让你皱眉头吗?让我们创建一个函数
isMouseInside
,如果鼠标在按钮上,它知道如何检查任何按钮对象,并返回true:var isMouseInside = function(btn) {
return (mouseX >= btn.x &&
mouseX <= (btn.x+btn.width) &&
mouseY >= btn.y &&
mouseY <= (btn.y+btn.height));
};
现在, 我们可以在
mouseClicked
中使用该函数,来大大减少重复的代码数量:mouseClicked = function() {
if (isMouseInside(btn1)) {
println("仍然没什么用");
} else if (isMouseInside(btn2)) {
println("第二个也没什么用!");
}
};
就是这样!我们已经使用函数来绘制多个按钮,并使添加新按钮变得相对容易。在下面尝试一下:
我们可以继续——创建程序中所有按钮的数组,实现定制按钮的标签和颜色——但希望这节课给了你一个很好的基础,你学会了如何使用函数创建简单的按钮。接下来,我们将介绍如何使用面向对象的原则创建按钮。