引言——如何在程序中改变绘制内容
回顾之前画懵逼脸的代码,可以看出,每个语句中都是直接用数值精确指定椭圆/直线/色彩:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// 函数setup() : 准备阶段
function setup() {
// 创建画布,宽度640像素,高度480像素
// 画布的坐标系统为,左上角坐标为(0,0),
// x方向水平向右,y方向垂直向下,单位像素
createCanvas(640,480);
}
// 函数draw():作画阶段
function draw() {
fill(255);// 填充白色
// 1 画脸
ellipse(320,240,200,200);// 圆圈
// 2 左眼
ellipse(280,240,50,50);// 另一个圆圈
// 3 右眼
ellipse(360,240,50,50);
// 4 嘴巴
ellipse(320,300,80,40);
fill(0);// 填充黑色
// 5 左眼珠
ellipse(280,240,20,20);
// 6 右眼珠
ellipse(360,240,20,20);
// 7 头发:从左到右画一排竖线
line(260,180,260,100);
line(280,180,280,100);
line(300,180,300,100);
line(320,180,320,100);
line(340,180,340,100);
line(360,180,360,100);
line(380,180,380,100);
}
|
例如,画脸的语句“ellipse(320,240,200,200);”中,椭圆的中心点位置(320,240)和长(200)宽(200)都是用精确的数值来指定的。如果我们希望它的位置为鼠标的实时位置,这种方式就不行了。但通过简单改造代码即可做到,即吧这句改为“ellipse(mouseX,mouseY,200,200);”,整段代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// 函数setup() : 准备阶段
function
setup
()
{
// 创建画布,宽度640像素,高度480像素
// 画布的坐标系统为,左上角坐标为(0,0),
// x方向水平向右,y方向垂直向下,单位像素
createCanvas
(
640
,
480
);
}
// 函数draw():作画阶段
function
draw
()
{
fill
(
255
);
// 填充白色
// 1 画脸
ellipse(mouseX,mouseY,200,200);// 只有这一句改动了
// 2 左眼
ellipse
(
280
,
240
,
50
,
50
);
// 另一个圆圈
// 3 右眼
ellipse
(
360
,
240
,
50
,
50
);
// 4 嘴巴
ellipse
(
320
,
300
,
80
,
40
);
fill
(
0
);
// 填充黑色
// 5 左眼珠
ellipse
(
280
,
240
,
20
,
20
);
// 6 右眼珠
ellipse
(
360
,
240
,
20
,
20
);
// 7 头发:从左到右画一排竖线
line
(
260
,
180
,
260
,
100
);
line
(
280
,
180
,
280
,
100
);
line
(
300
,
180
,
300
,
100
);
line
(
320
,
180
,
320
,
100
);
line
(
340
,
180
,
340
,
100
);
line
(
360
,
180
,
360
,
100
);
line
(
380
,
180
,
380
,
100
);
}
|
运行效果如下:
p5.js程序框架运行机制
实际运行发现,卡通人物的脸部会始终随着鼠标位置移动,移动的轨迹也会记录在画布上。
为了理解上述现象,必须要解答两个问题:
问题1 draw()这个函数是如何调用的?
问题2 mouseX和mouseY是啥玩意?
draw()函数调用方式
我们先来探讨一下第一个问题。从之前的教程1.1节已经了解,setup()和draw()两个函数定义了画画的两个主要步骤,准备和作画。然而,根据之前对于“函数”的讨论,我们的代码中其实只是这两个函数的“函数定义",而非“函数调用”。也就是说,这段代码中只是详细说明了setup()和draw()两个”行为/方法/招数/功能”的具体执行过程,但却并没有表达出在何时何地“施放”它们俩。那么我们的程序怎么就自动施放了它们呢?其实这就是p5.js帮我们做好的事情。p5.js既提供一大堆写好的“函数/招数/功能/方法/行为“,同时也定义了一个”框架“(framework),也就是规定了程序的执行的方式如下:
可见,setup()只会在程序开始时运行一次,而draw()函数会反复运行直到”中止“。而我们在sketch.js这个文件中写的程序其实只是整个程序的一部分,setup()和draw()的实际调用是由这个”框架“来实现的。
理解了这个框架,就能理解在上述程序中,为何懵逼脸随着鼠标移动反复绘制——draw()函数反复执行,每次执行中,鼠标位置不同,所以每次画脸的位置也就不同了。
mouseX和mouseY是什么?——变量
下面再讨论第二个问题,即mouseX和mouseY是啥?
我们打开官网的reference页面(https://p5js.org/reference/),搜索“mouse”,可以看到这哥俩的参考页面链接:
打开其中一个,可以查看其解释:
其含义就是,“变量” mouseX始终包含当前鼠标的横坐标数值。
也就是说,mouseX是一个在程序运行过程中数值会发生变化的家伙,其数值是随着鼠标的横向移动而变化的。像这样的在程序运行过程中数值可以改变的量就称为变量(variable)。同理,mouseY也是一个变量,并且其数值随着鼠标的纵向移动而变化。相对的,我们之前那种直接指定的数值就叫做常量(constant),例如”ellipse(mouseX,mouseY,200,200);”中,第三四个参数就是用常量200来指定的。
现在,我们已经可以完整理解为何会发生上述现象。然而,光是脸部跟随着鼠标移动,效果很诡异,若能让整个脸部都跟随鼠标移动,那才算达到我们的效果。为了实现这个目的,我们还需要进一步改造上述程序,并且要用到“表达式”进行一些简单的四则运算,先看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// 函数setup() : 准备阶段
function
setup
()
{
// 创建画布,宽度640像素,高度480像素
// 画布的坐标系统为,左上角坐标为(0,0),
// x方向水平向右,y方向垂直向下,单位像素
createCanvas
(
640
,
480
);
}
// 函数draw():作画阶段
function
draw
()
{
fill
(
255
);
// 填充白色
// 1 画脸
ellipse
(
mouseX
,
mouseY
,
200
,
200
);
// 圆圈
// 2 左眼
ellipse
(
mouseX
-
40
,
mouseY
,
50
,
50
);
// 另一个圆圈
// 3 右眼
ellipse
(
mouseX
+
40
,
mouseY
,
50
,
50
);
// 4 嘴巴
ellipse
(
mouseX
,
mouseY
+
60
,
80
,
40
);
fill
(
0
);
// 填充黑色
// 5 左眼珠
ellipse
(
mouseX
-
40
,
mouseY
,
20
,
20
);
// 6 右眼珠
ellipse
(
mouseX
+
40
,
mouseY
,
20
,
20
);
// 7 头发:从左到右画一排竖线
line
(
mouseX
-
60
,
mouseY
-
60
,
mouseX
-
60
,
mouseY
-
140
);
line
(
mouseX
-
40
,
mouseY
-
60
,
mouseX
-
40
,
mouseY
-
140
);
line
(
mouseX
-
20
,
mouseY
-
60
,
mouseX
-
20
,
mouseY
-
140
);
line
(
mouseX
,
mouseY
-
60
,
mouseX
,
mouseY
-
140
);
line
(
mouseX
+
20
,
mouseY
-
60
,
mouseX
+
20
,
mouseY
-
140
);
line
(
mouseX
+
40
,
mouseY
-
60
,
mouseX
+
40
,
mouseY
-
140
);
line
(
mouseX
+
60
,
mouseY
-
60
,
mouseX
+
60
,
mouseY
-
140
);
}
|
从运行结果可见,整个脸部都会跟随鼠标移动了。
从代码可见,原先与绘制位置有关的数值都改为与变量mouseX和mouseY有关的量,例如语句”ellipse(mouseX-40,mouseY,50,50);”中,指定椭圆中心位置为(mouseX-40,mouseY),其中"mouseX-40"就是一个“表达式”,其含义是计算出mouseX减小40得出数值,所以这个语句可以解释为“在鼠标当前位置偏左40像素的位置画一个长宽都是50的椭圆。” 再如语句“line(mouseX+20,mouseY-60,mouseX+20,mouseY-140);”,含义就是画一个连接(mouseX+20,mouseY-60)和(mouseX+20,mouseY-140)两个点的线段,第一个点的位置(mouseX+20,mouseY-60)即鼠标位置偏右20像素/偏上60像素,第二个点的位置(mouseX+20,mouseY-140)即鼠标当前位置偏右20像素/偏上140像素。
关于”表达式”,我们在后续章节在专门讲解,这里可以简单理解为对用运算符对一系列变量和常量进行计算并得出结果的操作,它可以看作是程序的最基本组件。
相关资源
教程示例程序:https://github.com/magicbrush/DrawingByCodingTutorialDemos/