
交互式花园预览,你点击后,浅蓝色画布上会出现花朵。
在本教程中,你将使用 p5.js 来模拟一个交互式花园。在这个项目中,画布上会出现 20 朵随机生成的花朵,每朵花都有不同的颜色和大小。你还可以点击画布添加新的、独特的花朵,让花园更加丰富。每朵花都有凋谢动画,会逐渐缩小直至消失,就像花朵自然凋谢一样。
在本教程中,你将:
- 通过定义、创建和使用自定义对象,了解JavaScript 对象等数据结构。
- 学习使用对象方法操作JavaScript 数组等内置对象。
- 使用 for 循环遍历多个对象。
准备工作
开始之前,你应该能够:
- 使用 p5.js 在画布上添加和自定义形状和颜色
- 声明、初始化、使用和更新自定义变量以及带有参数和返回值的函数
- 在画布上添加动画
- 注释代码并处理错误信息
- 创建和使用自定义函数
- 理解如何使用 for 循环来管理重复性任务
如需了解有关数组、循环和自定义函数的更多信息,请访问使用函数组织代码和使用循环重复教程。
- 在变量和变化教程中,你学习了如何使用变量存储数据。变量可用于存储单个值,例如数字或文本。
- 在使用媒体对象制作动画教程中,你学习了如何使用 p5.js 内置的
p5.Image和p5.Graphics对象。这些对象将数据(例如图像)与称为方法的特殊函数捆绑在一起,使你能够对这些数据执行操作。
在本教程中,你将学习如何:
- 定义对象
- 创建和使用自定义函数来更新和绘制对象
- 使用数组管理多个对象
让我们开始吧!
JavaScript 对象
我们已经了解到,变量一次只能存储一个值(数字、字符串或布尔值)。数组可以通过索引存储多个值。然而,有时我们希望将关于同一事物的多个相关数据存储在一起。我们可以使用JavaScript 对象来实现这一点,它们通过将属性和值配对来将数据捆绑在一起。属性是与对象关联的特殊类型的变量或名称。每个属性都存储一个值。例如,如果我们想创建一个基于花朵的 JavaScript 对象,它可以拥有描述其位置(x 和 y)以及表情符号的属性。
步骤 1:创建你的第一个对象
让我们从创建一朵花开始,打造我们的花园。我们可以先用表情符号来绘制它,所以我们需要花朵的 x 坐标、y 坐标以及表情符号的数据。
新建一个 p5.js 项目,并将其命名为“Data Structure Garden”(数据结构花园)。保存项目。
通过将花朵的画布坐标与用于绘制它的表情符号绑定,创建一个花朵对象。我们将该对象存储在一个名为
flower的变量中。在setup()函数上方添加以下代码:// 创建 flower 对象。 let flower = { x: 200, y: 100, emoji: '🌸' };在
setup()函数中添加以下代码行,即可将对象打印到控制台:// 在控制台中打印 flower 对象。 console.log(flower);将背景色设置为
"lightblue"。
你的代码应该如下所示:
尝试在 p5.js 网页编辑器中打开目前为止的代码。你应该能在控制台中看到你的花朵对象。注意,你可以点击控制台中的箭头来查看对象的属性和值。
以下是定义对象的一般语法:
// 定义一个对象。
let objectName = {
property1: value1,
property2: value2,
property3: value3
};
对象语法包括:
大括号 (
{}) 括起一组属性;属性可以是名称,例如变量或函数的名称,也可以是字符串。在属性名称中使用空格时,使用字符串通常会很有帮助。例如,我可能会创建一个具有“名字”和“姓氏”属性的对象,如下所示:
let ironMan = { "first name" : "Tony", "last name" : "Stark" }
使用冒号 (:) 分隔每个属性及其对应的值;
- 值可以是任何数据类型,甚至可以是函数!
使用逗号 (,) 分隔每对
property : value。
| 对象名称 | ||
| | |
| | | | |
| 属性 | 值 |
| | | | |
| 属性 | 值 |
| | | | |
| 属性 | 值 |
|
你可以通过在定义对象时初始化新变量来存储对象。在上面的代码中,你声明了一个名为 flower 的变量。然后,你使用一个对象初始化了 flower 变量,该对象的属性代表一朵可以绘制在画布上的花。
要检查对象的属性和值,你可以使用 console.log(),它会在控制台中显示以下内容:
▼ {x: 200, y: 100, emoji: "🌸"}
x: 200`
y: 100`
emoji: "🌸"
步骤 2:使用你的对象
你可以使用变量名、属性名和点号表示法来访问存储在对象属性中的任何值,如下所示:
// 使用点号表示法访问 objectName 对象的 property1 属性值。
objectName.property1
通过将花朵对象的属性传给
text()函数,在画布上绘制花朵。在
draw()函数中,添加以下代码:// 显示花朵表情符号。 text(flower.emoji, flower.x, flower.y);
你可以在
text()函数之前使用textSize()来设置花朵的大小。// 增大文本/表情符号的大小。 textSize(100);
你的代码应该如下所示:
要访问花朵对象中的值,我们使用点号表示法,通过点运算符来访问对象中的属性和值。
在上面的代码中,你访问了:
- 花朵的表情符号(
flower.emoji) - 花朵的 x 坐标(
flower.x) - 花朵的 y 坐标(
flower.y)
由于这些值是字符串和数字,因此可以像普通变量一样,直接在 text() 函数中使用它们。
访问 p5.js 参考文档,了解更多关于对象的信息。
步骤 3:定义 createFlower() 函数
现在,让我们定义一个可以在画布上显示不同花朵的函数。我们将定义一个 createFlower() 函数,以便自定义我们创建的花朵对象。我们需要确保可以更改不同花朵的 x 和 y 坐标,以及它们的大小和颜色。一种方法是为花朵对象添加 size 和 color 属性。在这个项目中,我们还希望花朵出现一段时间后消失。为此,我们将为花朵对象添加一个 lifespan 属性。
我们希望调用 createFlower() 时能返回信息,就像内置的 text() 函数会在画布上显示文本一样。因此,我们必须指定一个返回值。使用返回值是将函数计算出的信息保存到变量或数组中的好方法。
在这个例子中,我们将使用 random() 来为绘制在画布上的花朵创造多样性。
定义一个
createFlower()函数,它会创建一个随机花朵对象。每次调用createFlower()时,所有值都会随机设置。使用
createFlower()函数创建一个花朵对象,并将其存储在名为myFlower的变量中,以此测试该函数。在
draw()函数中添加以下代码:// 创建 flower 对象。 let myFlower = createFlower();
使用
myFlower对象的属性在画布上绘制一个椭圆。在
draw()函数中添加以下代码:// 使用 myFlower 对象的属性绘制椭圆。 fill(myFlower.color); ellipse(myFlower.x, myFlower.y, myFlower.size);
通过将帧率设置为每秒一帧,降低每朵花出现的速度。在
setup()函数中,添加:frameRate(1);。
你的代码应如下所示:
在上面的代码中,你使用 createFlower() 创建并返回一个具有随机值的新花朵对象,然后将该对象存储在变量 myFlower 中。随后可以通过 myFlower 访问这些随机生成的属性值,并用它们在画布上绘制椭圆。每次运行 draw() 时,都会创建一个新的随机花朵对象,并使用该对象的属性在画布上绘制一个新的随机椭圆。
访问 p5.js 参考文档,了解有关 return 返回值和 random() 的更多信息。
步骤 4:定义带参数的 drawFlower() 函数
既然我们知道 createFlower() 会创建并返回一个新的花朵对象,接下来让我们定义 drawFlower() 函数。我们的 drawFlower() 函数会接收一个花朵对象作为参数,并将其绘制到画布上。这有助于使我们的代码更清晰易懂。
在所有其他函数之外,定义一个
drawFlower()函数,它接收一个花朵对象作为参数。function drawFlower(flower) { }在
drawFlower()函数内部,画出花朵的各个部分,包括花瓣和花心。请记住使用花朵对象的x和y坐标、size和color属性。你可以将以下代码添加到函数体中:noStroke(); fill(flower.color); // 绘制花瓣。 ellipse(flower.x, flower.y, flower.size / 2, flower.size); ellipse(flower.x, flower.y, flower.size, flower.size / 2); // 绘制黄色花心。 fill(255, 204, 0); circle(flower.x, flower.y, flower.size / 2);接下来,我们将测试
drawFlower()函数。请将以下代码添加到draw()函数中:// 测试 drawFlower() 函数。 let flower1 = createFlower(); drawFlower(flower1);
你的代码应该类似于这样:
在上面的代码中,createFlower() 用于创建并返回一个随机花朵,该花朵存储在 flower1 中。然后,把 flower1 传给 drawFlower(),它会读取花朵对象的属性,在画布上画出花朵。每次运行 draw() 函数时,这个过程都会重复,因此画布上的花朵会随机出现和消失。
步骤 5:学习使用数组
在开始使用数组创建花园之前,让我们先通过几个简单的例子更好地理解数组的工作原理。JavaScript 数组是一种对象,它可以在一个变量名中存储多个值。数组中的每个值都是一个元素,每个元素都有一个特定的位置,称为索引。任何JavaScript 数组中第一个元素的索引始终为 0。
示例 1:创建并绘制花名数组
| | | | | | |
| 0 | 1 | 2 |
在这个例子中,我们使用方括号([ ])和逗号创建了一个数组,逗号用于分隔数组中的每个元素。该数组是一个字符串集合,每个字符串代表一种花朵,并存储在名为 flowers 的变量中。数组中的每个元素都包含一个花朵名称的字符串。可以通过索引号访问每个元素。例如, "Rose" 位于索引 0, "Daisy" 位于索引 1, "Tulip" 位于索引 2。
| 用于保存元素的变量名 | 要访问的数组 | 数组中的索引 | ||||
| | | | | | |
在上面的代码片段中,我们使用索引表示法 flowers[0] 访问了 flowers 数组中索引为 0 的第一个元素。在这个例子中,我们使用这种语法在画布上放置文本,文本名称取自 flowers 数组中的各个元素。
然后,我们将该值赋给一个名为 rose 的新变量。rose 现在存储的是字符串 "Rose",也就是 flowers 数组中的第一个花名。其他元素中存储的值也可以用同样的方法访问。
示例 2:使用 for 循环遍历花名数组
我们的第二个例子使用 for 循环遍历数组中的每个元素,并将文本绘制到画布上。遍历是指对数组中的每个元素重复执行一段代码的过程。
| 从索引 0 开始 | ……直到数组末尾 | |||
| | | | |
| | |
获取索引 i 处的元素 |
|
为了遍历数组,上面的代码片段使用了一个 for 循环来遍历 flowers 数组中的每个元素。在这个 for 循环中,索引 i 从 0 开始,每轮加 1,直到达到比数组元素个数少 1 的数为止——因为数组索引从 0 开始,最后一个元素的索引就是元素个数减 1。可以用 array.length 属性获取任何数组的元素个数。在上面的 for 循环中,我们使用 flowers.length 获取 flowers 数组的长度。为了访问数组中的值,我们在 for 循环中使用 flowers[i],并在 text() 函数中将每个元素的值以文本形式显示在画布上。
示例 3:向数组中添加新元素
| | |
| 新元素会添加到这里 |
| | |
将新元素添加到 flowers 数组末尾 |
使用数组方法 array.push(),你可以将新元素添加到数组末尾。在本例中,我们将 "Sunflower" 添加到 flowers 数组的末尾。
示例 4:删除数组元素
| | | | | | |
| 0 | 1 | 2 |
| | | | |
| 从这个索引开始 | ……删除一个元素 |
要从数组中删除元素,我们使用 array.splice() 函数。array.splice() 函数接收两个参数来修改 flowers 数组:一个是起始索引,另一个是要删除的元素数量。例如,flowers.splice(0,1) 会从数组中删除 Rose 元素,只留下 Daisy 和 Tulip 元素。
示例 5:使用 for..of 循环遍历数组
| 声明一个变量来存储当前元素 | 遍历数组的关键字 | 要遍历的数组 | ||||
| | | | | | |
| | |
| 对当前元素执行操作 |
|
我们还可以使用 for..of 循环来遍历数组。在循环过程中,flower 变量会临时存储每个元素。循环中的 print(flower) 语句会将每个花名依次打印到控制台,即“Rose”、“Daisy”和“Tulip”。
当代码变得更加复杂时,使用 for..of 循环可以使代码更易于阅读,例如以下示例:使用索引的 for 循环与for..of 循环。
步骤 6:使用数组生成多朵花
现在,是时候使用数组在屏幕上生成多朵花了。
首先,在程序顶部声明
flowers数组。空方括号[]表示一个空的 flowers 数组。// 创建 flowers 数组。 let flowers = [];在所有其他函数之外,定义
flowerPower()函数。// 创建 20 个花朵对象的函数。 function flowerPower() {}在
flowerPower()函数内部,使用for循环创建 20 个花朵对象,并将它们添加到flowers数组中。for(let i = 0; i < 20; i += 1) { // 在随机位置创建一朵花。 let flower = createFlower(); // 将花朵添加到 flowers 数组中。 flowers.push(flower); }在
setup()函数中调用flowerPower()函数来创建 20 个花朵对象。// 生成 20 个花朵对象。 flowerPower();在
draw()函数中使用for..of循环遍历数组,绘制flowers数组中的每朵花。for..of循环的意思是,对于flowers数组中的每个花朵对象,调用drawFlower()函数。// 遍历 flowers 数组中的每一朵花。 for (let flower of flowers) { drawFlower(flower); }
你的代码应该类似于这样:
步骤 7:更新数组中的花朵
现在我们已经测试了 flowers 数组,让我们绘制凋谢效果。使用数组控制花朵的好处之一是能够更新每朵花中的值。
在所有其他函数之外,定义一个名为
updateAndDrawFlowers()的函数:function updateAndDrawFlowers() {}这个函数会遍历数组中的每个花朵对象,并逐一绘制。
for (let flower of flowers) { drawFlower(flower); }在
for循环内,减小花朵对象的size和lifespan以模拟凋谢。添加一个凋谢动画效果,在
drawFlower()函数绘制完花朵后,花朵尺寸会缩小 1%。我们把当前尺寸乘以 0.99 再存回花朵的size属性,以此实现凋谢效果。然后把花朵的生命周期减 1,同样存回花朵的lifespan属性。使用乘法赋值运算符和递减运算符,语法如下:// 让花朵逐渐凋谢。 flower.size *= 0.99; // 减少生命周期。 flower.lifespan -= 1;当花朵对象的生命周期为零或负数时,我们将使用
array.splice()方法将其从数组中移除。for (let flower of flowers) { // 绘制花朵。 drawFlower(flower); // 让花朵逐渐凋谢。 flower.size *= 0.99; // 减少生命周期。 flower.lifespan -= 1; if (flower.lifespan <= 0) { // 保存花朵的索引。 let i = flowers.indexOf(flower); // 移除已凋谢的花朵。 flowers.splice(i, 1); } }在
draw()函数中添加updateAndDrawFlowers()。从
setup()函数中移除对frameRate(1)的调用。
你的代码应该类似于这样:
步骤 8:使用 mousePressed() 向数组添加更多花朵
最后,让我们使用 mousePressed()、mouseX 和 mouseY 向画布添加更多花朵。每次点击画布时,都会向花朵数组中添加一个新的花朵对象。
在所有其他函数之外,编写你的
mousePressed()函数:function mousePressed() { let flower = createFlower(); // 将 x 重新赋值为 mouseX。 flower.x = mouseX; // 将 y 重新赋值为 mouseY。 flower.y = mouseY; // 将花朵添加到 flowers 数组中。 flowers.push(flower); }
你的代码应该类似于这样:
总结
恭喜你完成数据结构花园教程!
在本教程中,你学习了 JavaScript 对象和数组的基本概念,并用它们创建了一个动态、可交互的花园。使用数组可以帮助你高效管理多个对象及其各自的属性和行为。这是编程中一项很实用的技能,也为之后构建更复杂的交互程序打下基础。
下一步
- 在创建和设置 HTML 样式教程中,使用 p5.js 和 HTML 及 CSS。
- 在Abracadabra:用手说话教程中,使用 p5.js 和ml5.js。
- 在Node.js 入门和简单旋律应用教程中,使用 p5.js 和Node.js。
再次祝贺你完成了本教程。继续尝试,继续编码,最重要的是,享受学习和创作的乐趣!