引言——想要重复绘制内容应该怎么办?

目前我们已经清晰地理解了如何用代码绘制1个懵逼脸。

现在升级一点难度,考虑一个问题:

如果要在屏幕上不同位置画N个懵逼脸,怎么办?

若采用之前一样的方法,我们可以在绘制完一个脸后,用重复的代码(不同参数)绘制另一个脸,比如:

 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
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
// 函数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);
// 以下为重复代码,但参数不同
// 画第二个脸
fill(255);// 填充白色
// 1 画脸
ellipse(220,240,200,200);// 圆圈
// 2 左眼
ellipse(180,240,50,50);// 另一个圆圈
// 3 右眼
ellipse(260,240,50,50);
// 4 嘴巴
ellipse(220,300,80,40);
fill(0);// 填充黑色
// 5 左眼珠
ellipse(180,240,20,20);
// 6 右眼珠
ellipse(260,240,20,20);
// 7 头发:从左到右画一排竖线
line(160,180,160,100);
line(180,180,180,100);
line(200,180,200,100);
line(220,180,220,100);
line(240,180,240,100);
line(260,180,260,100);
line(280,180,280,100);
}

这样就能绘制两个脸:

但是......我想画20个脸,咋办?????

不嫌累的话可以试试写20遍重复代码......,希望不要勾起被老师罚抄课文的苦难记忆......

可见,这个办法的局限也是相当明显的。

用自定义函数实现功能复用

为了解决这个麻烦,我们需要获得一项新技能:自定义函数

回顾一下1.1节中讲到的“函数”,或者说“招数/方法/行为/职能/功能”,只要有了某个函数定义,也就是概括了某种做事的流程,然后在我们想要的地方便可以“调用”它,或者说是“施放/执行/行使/发招”。例如,我们可以设想已经定义了一个“DrawConfuseFace”的函数,然后,在draw()函数中就可以直接调用这个函数两次,实现画两个懵逼脸。也就是,我们现在期望的draw()函数写法:

// 函数draw():作画阶段
function draw() {
// 在(200,140)位置画第一个脸
drawConfuseFace(200,140);
// 在(320,280)位置画第二个脸
drawConfuseFace(320,280);
}

这当然是可行的!而且实现后效果如下:

为了实现这个drawConfuseFace函数,我们要依次搞清楚3项技能: 1.自定义函数;2.自定义变量;3.函数参数

自定义函数

在1.1节的讲解中,已经了解到“函数定义”的基本办法,也就是现有代码中函数setup()和draw()的写法即函数定义。

那么,我们仿照draw()函数的写法,在其后新写一个函数,并在draw()函数中直接调用它20遍!希望能画出20个懵逼脸。

代码如下:

// 函数setup() : 准备阶段
function setup() {
// 创建画布,宽度640像素,高度480像素
// 画布的坐标系统为,左上角坐标为(0,0),
// x方向水平向右,y方向垂直向下,单位像素
createCanvas(640,480);
}
// 函数draw():作画阶段
function draw() {
// 调用20遍,希望能画20个脸
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
drawConfuseFace();
}
// 自定函数:画懵逼脸
function drawConfuseFace() {
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);
}

然而,却只能画出来1个脸:

这是为啥?原来,我们虽然施放了20次drawConfuseFace(),但由于每一次施放中,drawConfuseFace()函数的执行过程用到的参数完全一样,因此,上述代码相当于在相同位置重复画了20次,每一次绘制都覆盖掉前一次绘制的结果,那么最终也只能看到1个脸。
这并非我们想要的函数形态,我们更希望drawConfuseFace()能够在不同位置画脸。
于是,drawConfuseFace()函数需要进行改造,让它能够在释放时输入参数,并且根据参数的具体数值来发挥不同的效果,
例如,我们希望施放该函数时可以用以下形态:
drawConfuseFace(100,200); //在(100,200)位置画懵逼脸
drawConfuseFace(300,50); // 在(300,50)位置画懵逼脸
......
为了实现它,还需要对变量的认识更进一步,即学会“自定义变量”。

自定义变量

在之前实现的用鼠标控制位置的代码中,我们用到了两个变量mouseX和mouseY,它们是p5.js提供的可以在任意时刻任意位置直接访问的变量。为了更加灵活地指定位置,我们可以用自定义变量。

下列代码示例了在程序中自定义变量:

var A,a; // 定义两个变量 A和a, 特别注意,大小写不同的名称对应不同变量!
var B = 1; // 定义变量B,赋值1
var Haha = 100; // 定义变量Haha, 赋值100;
var Jam = B; // 定义变量Jam, 用变量B的值为其赋值;

在定义了上述变量后,便可以在需要的时候使用它们,就如同使用变量mouseX和mouseY一般,例如:

ellipse(B,Haha,100,100); // 在位置(B,Haha)绘制长宽为100的圆形
line(Jam,Haha,Haha,B); // 从(Jam,Haha)到(Haha,B)绘制一条线段

此外,在变量定义中,用到了 赋值符"=",要注意,它的作用有点像是“等于”,但其实应该理解为“赋值”,其目的是将右边的表达式的计算结果赋予左边的变量,下面列出一些用法示例:

var A = 1; // 定义变量A并赋值1
var B = 2; // 定义变量B并赋值2
var C = A + 10;// 定义变量C, 计算表达式A+10的值,然后赋值给C
var D = B - 5; // 定义变量D, 计算表达式B-5的值,然后赋值给D
var E = A + B; // 定义变量E, 计算表达式A+B的值,然后赋值给E

掌握了自定义变量,我们对原来的代码进行改造。原始代码为:

  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);
}

用自定义变量对其进行改造:

  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
 40
 41
 42
 43
 44
// 函数setup() : 准备阶段
function setup() {
// 创建画布,宽度640像素,高度480像素
// 画布的坐标系统为,左上角坐标为(0,0),
// x方向水平向右,y方向垂直向下,单位像素
createCanvas(640,480);
}
// 函数draw():作画阶段
function draw() {
// 定义两个变量,并对其赋值为当前鼠标坐标
// 之后的所有语句都用这两个变量来设定绘制位置
var centerX = mouseX;
var centerY = mouseY;
fill(255);// 填充白色
// 1 画脸
ellipse(centerX,centerY,200,200);// 圆圈
// 2 左眼
ellipse(centerX-40,centerY,50,50);// 另一个圆圈
// 3 右眼
ellipse(centerX+40,centerY,50,50);
// 4 嘴巴
ellipse(centerX,centerY+60,80,40);
fill(0);// 填充黑色
// 5 左眼珠
ellipse(centerX-40,centerY,20,20);
// 6 右眼珠
ellipse(centerX+40,centerY,20,20);
// 7 头发:从左到右画一排竖线
line(centerX-60,centerY-60,centerX-60,centerY-140);
line(centerX-40,centerY-60,centerX-40,centerY-140);
line(centerX-20,centerY-60,centerX-20,centerY-140);
line(centerX ,centerY-60,centerX ,centerY-140);
line(centerX+20,centerY-60,centerX+20,centerY-140);
line(centerX+40,centerY-60,centerX+40,centerY-140);
line(centerX+60,centerY-60,centerX+60,centerY-140);
}

如此改造后,程序运行的结果并未变化,也就是绘制一个跟随鼠标运动的懵逼脸。

但这样改造仍然有两个好处,一方面是具备了更佳的可读性,因为我们定义的变量名称centerX和centerY本身就使用了具有明确含义的词汇center,其名称就说明了它们是绘制的懵逼脸的“中心”位置;另一方面,代码具有了更好的可扩展性:这两个变量的数值可以在后续过程中任意时刻发生变化,而不会像mouseX和mouseY那样始终是鼠标位置;并且,重点是,这种改造便于我们将这一段代码改造为带参数的函数。(●ˇ∀ˇ●)

函数参数

函数在定义时,可以定义任意数量的参数,例如,我们可以将drawConfuseFace()函数定义为带参数形态:
function drawConfuseFace(posX, posY) 
// 定义了两个形式参数posX,posY,它们也是函数中的两个变量
// 函数执行过程中所有绘图语句都基于变量posX和posY来指定位置
{
fill(255);// 填充白色
// 1 画脸
ellipse(posX,posY,200,200);// 圆圈
// 2 左眼
ellipse(posX-40,posY,50,50);// 另一个圆圈
// 3 右眼
ellipse(posX+40,posY,50,50);
// 4 嘴巴
ellipse(posX,posY+60,80,40);
fill(0);// 填充黑色
// 5 左眼珠
ellipse(posX-40,posY,20,20);
// 6 右眼珠
ellipse(posX+40,posY,20,20);
// 7 头发:从左到右画一排竖线
line(posX-60,posY-60,posX-60,posY-140);
line(posX-40,posY-60,posX-40,posY-140);
line(posX-20,posY-60,posX-20,posY-140);
line(posX ,posY-60,posX ,posY-140);
line(posX+20,posY-60,posX+20,posY-140);
line(posX+40,posY-60,posX+40,posY-140);
line(posX+60,posY-60,posX+60,posY-140);
}

如此一来,我们便可以在draw()函数中以带参数的形态调用drawConfuseFace()了:

// 函数draw():作画阶段
function draw() {
// 在(200,140)位置画第一个脸
drawConfuseFace(200,140);
// 在(320,280)位置画第二个脸
drawConfuseFace(320,280);
}
并且,drawConfuseFace()可以反复以调用并输入不同数值的参数,从而在不同位置画出懵逼脸了。
现在反过来对drawConfuseFace()函数的带参数形式进行解释:
其定义"function drawConfuseFace(posX,posY)"中, posX和posY是函数的形式参数,简称形参。这里相当于是说:有一个行为叫做drawConfuseFace,它能够以某个位置(posX,posY)为中心画一个懵逼脸,并且在实际执行这个行为的时候必须对位置(posX,posY)指定具体数值,而具体指定的数值就叫做函数调用时的实际参数,简称实参
有了形式参数posX和posY,它们便成为了在该函数的执行过程中(也就是大括号{}中的代码)可以访问的变量。例如,函数中的语句”ellipse(posX,posY,200,200);"便是以(posX,posY)为中心绘制椭圆,而且其后所有的绘图语句都是基于这两个变量来指定位置。
为了进一步理解函数的形式参数和实际参数,我们可以类比生活中的一些例子。例如,手机有一个功能叫做“拨号”,这个行为在手机中已经有一套完整的执行流程,其中需要用到一个”号码”的形式参数,并且在人们实际使用手机执行该功能时,就需要具体指定“号码”的数值,也就是指定的实际参数。再例如,有一种行为叫做“睡觉”,这个行为有一套标准的执行过程,相当予“睡觉”行为的函数定义,它在未实际执行时,睡多长时间时一个不确定的量,也即一个形式参数,而在某人实际执行”睡觉”行为时,就需要具体化睡眠时间,例如8小时,也就是指定了实际参数为8小时。
有了这个自定义的“招数” drawConfuseFace(posX,posY),我们便可以尝试直接调用它,从而方便地画出多个懵逼脸,例如,将draw()函数中的代码写为:
  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
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
// 函数setup() : 准备阶段
function setup() {
// 创建画布,宽度640像素,高度480像素
// 画布的坐标系统为,左上角坐标为(0,0),
// x方向水平向右,y方向垂直向下,单位像素
createCanvas(640,480);
}
// 函数draw():作画阶段
function draw() {
// 在鼠标位置及其上下左右绘制5个懵逼脸
drawConfuseFace(mouseX-100,mouseY);
drawConfuseFace(mouseX+100,mouseY);
drawConfuseFace(mouseX,mouseY-100);
drawConfuseFace(mouseX,mouseY+100);
drawConfuseFace(mouseX,mouseY);
}
function drawConfuseFace(posX, posY)
{
fill(255);// 填充白色
// 1 画脸
ellipse(posX,posY,200,200);// 圆圈
// 2 左眼
ellipse(posX-40,posY,50,50);// 另一个圆圈
// 3 右眼
ellipse(posX+40,posY,50,50);
// 4 嘴巴
ellipse(posX,posY+60,80,40);
fill(0);// 填充黑色
// 5 左眼珠
ellipse(posX-40,posY,20,20);
// 6 右眼珠
ellipse(posX+40,posY,20,20);
// 7 头发:从左到右画一排竖线
line(posX-60,posY-60,posX-60,posY-140);
line(posX-40,posY-60,posX-40,posY-140);
line(posX-20,posY-60,posX-20,posY-140);
line(posX ,posY-60,posX ,posY-140);
line(posX+20,posY-60,posX+20,posY-140);
line(posX+40,posY-60,posX+40,posY-140);
line(posX+60,posY-60,posX+60,posY-140);
}

其效果如下:

补充:变量的作用域和生命周期

为了用好变量,还需要了理解它的作用域生命周期

一个变量的作用域就是指在程序代码的哪些部分能够访问到这个变量,从下图中可以看出作用域的几种典型情况:

执行效果如下:

有关作用域,可以查看这里的解释:http://www.runoob.com/js/js-scope.html

知识点

函数:http://www.runoob.com/js/js-functions.html

作用域:http://www.runoob.com/js/js-scope.html

相关资源

教程示例程序:https://github.com/magicbrush/DrawingByCodingTutorialDemos/

1.5 开始第一幅“码绘”——自定变量与函数,创一招“懵逼表情涂”相关推荐

  1. p5.js 我的第一幅码绘——小丑

    p5.js 我的第一幅码绘--小丑 码绘相比较于手绘肯定是要繁琐,但优势在于可以做一些有意思的动态效果和交互. 效果图 小交互--癫狂状态的小丑 DC漫画里,小丑是个癫狂的角色,令人战栗.让我们展现一 ...

  2. 手绘与码绘的比较——实战之梵高《星空》

    手绘与码绘的比较--实战之梵高<星空> 版权声明:本文为博主原创文章,未经博主允许不得转载. 最近博主的老师在交互媒体设计课上布置了一个作业,是关于"运动"主题的作品创 ...

  3. 码绘VS手绘(一) 编程与手绘的对比

    前言 本文主要讲解在静态图方面如何分别通过手绘和码绘两种方式来表现,以及它们之间的差异与联系,谨以此博客记录学习过程.博主新手一枚,这是博主第一次写博文,若有不足之处,还请温柔拍打~~(笔芯~~) 手 ...

  4. 【动态】码绘VS手绘的对比——有点萌的开关

    [动态]码绘VS手绘的对比--有点萌的开关 背景简介 主题 成果展示 前期分析 制作过程 体会与思考 参考资料 背景简介 上一次简单的通过静态的手绘与码绘对两者进行了一些简单的对比,这一次更加好玩了, ...

  5. 码绘与手绘——表达动态

    一.手绘表现动态: 这个题目一开始看来 ,手绘是处于非常劣势的地位的,因为电脑上可以实现动态的过程,就是用动画的原理,每秒绘制60帧,不断进行重绘图形,来达到运动的效果的. 而本身来讲,手绘的图片本质 ...

  6. 码绘VS手绘(二)动态绘图

    码绘VS手绘(二)动态绘图 一.前言 二.实验内容 (一)主题 (二)实验结果 1.码绘 最终效果 程序结构 具体函数解析 2.手绘 三.总结--编程与手绘的比较 1.工具和载体 2.技法 3.理念 ...

  7. 手绘与码绘的比较---模拟风吹树动

    手绘与码绘的比较-模拟风吹树动 一.内容介绍 本文主要介绍用手绘和码绘两种方式模仿一种自然现象–风吹树动的过程,以及在实现过程对这两种方式的比较和思考.之所以选择风吹树动,是因为每次速写画一些场景时( ...

  8. 手绘VS码绘——动态篇(视觉错觉)

    前言 在介绍这次的实验前,先让我们看看一组图片: 相信大家看完这组图片已经有点难受的想点一下右上角的X了,其实不止你们,我在写这篇文章的时候也想关掉它-- 接下来我就按照正常的介绍流程告诉大家,这些图 ...

  9. 手绘 v.s. 码绘(1.2 实践报告)

    目标 多角度比较手绘与码绘:技法.工具.理念.创作体验.呈现效果.载体.局限性.应用-- 思考深刻,理论完善,参考资料详实规范,必须引用文末的参考文章 使用各种论证方法,体现严谨清晰的逻辑 按时间线对 ...

最新文章

  1. 【学习笔记】Android 图像处理
  2. 支持鸿蒙的手机型号多少钱,华为鸿蒙系统支持手机型号一览
  3. linux 内核发布时间,求问Linux最新内核版本以及发布日期。
  4. 一个简单词法分析器的实现代码(java实现)
  5. 关于Oracle回收站的一些操作
  6. C#编程尽量使用接口(转)
  7. python源码文件以什么格式结尾结尾_查看python源码,发现里面的函数都以pass结尾,那么意义何在?...
  8. 隐藏文件或文件夹属性无法修改解决方案
  9. Net 4.0并行库实用性演练
  10. java中如何定位文本框_div定位在文本框下
  11. 移动开发者走向全能开发者的五大技能
  12. 联想g510升级换什么cpu好_联想G510笔记本完全拆机指南(图解)
  13. C语言中的EOF是什么?
  14. Android水波纹效果
  15. 先进的半导体晶圆清洗技术
  16. Bootstrap3 按钮状态切换
  17. 教你怎么解决Linux依赖包第一篇:pkgs.org资源网下载
  18. 【Java】JVM内存回收
  19. oracle 数据泵 network link使用
  20. ImportError: cannot import name ‘ModelOutput‘ from ‘smplx.body_models‘

热门文章

  1. ios pod的使用遇到 library not found for -lPods
  2. SAR成像处理软件运行环境配置(AGX平台)
  3. $vjudge-$基本算法专题题解
  4. react 项目环境搭建和ant design组件使用全攻略详解
  5. Jenkins自动化持续集成之curl
  6. Stata:非对称固定效应模型
  7. PAT日志 1042
  8. JavaScript-狂神说笔记
  9. 移动网上大学itch连接mysql_中国移动网上大学登录(移动网上大学客户端)
  10. Web搜索引擎工作原理和体系结构