版权声明:本文为博主原创文章,未经博主允许不得转载。
第一章 您好:了解p5.js
第二章 开始写代码:创建你的第一个p5.js程序
第三章 画:定义和画简单的形状
第四章 变量: 存储、修改和再利用数据
第五章 响应: 利用鼠标、键盘和触摸板控制和影响程序
第六章 平移、旋转、缩放:坐标的变换
第七章 多媒体: 加载和显示多媒体,包括图片和字体
第八章 运动: 移动图形并为其设计“舞步”
第九章 函数: 建立新的代码模块
第十章 对象: 创建含有变量和函数的代码模块
第十一章 数组: 让处理一列变量变得简单
第十二章 数据: 载入并可视化数据
第十三章 延伸: 了解声音的DOM
3、画
首先要说明的是,在电脑屏幕上作画与在纸上作画是类似的。通常这个过程始于一个谨慎的技术性操作,但是随着新的内容不断引入,这个过程从用软件画简单的图形扩展到了动画和交互。在我们做出这个飞越之前,我们需要从头开始。
一个电脑屏幕是由一堆称之为像素的发光点组成的网格。每个像素在网格中的位置我们定义为像素的坐标。当你创建一个p5.js项目时,你将会用浏览器来查看它。在浏览器的窗口中, p5.js 创建了一个画布,你所创建的图形都将绘制在这个画布上。这个画布可能和窗口尺寸相同,或者可能有不同的维度。这个画布通常默认被放置在你窗口的左上角,但是你可以把它放置在窗口的其他位置。
当你在画布上画图时,x坐标表示的是到画布左边缘的距离而y坐标表示的是到画布上边缘的距离。我们把一个像素的坐标写成(x,y)。因此,如果一个画布的大小是 200*200像素,左上角的坐标为(0,0),中心位置的坐标为(100,100),右下角的坐标就是(199,199)。这些数字看起来会让你感觉很迷惑:为什么我们要用 0到199 来代替 1到200 呢?这是因为在程序中,我们通常从0开始计数。在之后的内容中你会发现,这样计数计算起来更简单。
画布
创建画布和将图案画在画布上的过程都是通过 函数(functions)这个代码元素实现的。 函数是p5.js程序的基本的构成部分。函数的行为是通过它的参数定义的。例如,几乎每一个p5.js程序都有一个 “createCanvas()”函数用来创建一个特定宽高的画布。如果你的程序没有这个函数,那么p5.js默认会创建一个 100*100像素的画布。
例子3-1:创建一个画布
createCanvas() 函数有两个参数:第一个参数是画布的宽,第二个参数是画布的高。为了画一个800像素宽、600像素高的画布,我们需要输入如下代码:
function setup() {
createCanvas(800, 600);
}
运行这行代码看看结果。将其中的数字变为其他值看看会发生什么。试一下用非常小的数字或者大于你屏幕大小的数字会有什么效果。
例子3-2:画一个点
我们使用 point() 函数在画布上绘制一个有颜色的像素点。这个函数用两个参数定义一个位置,即x坐标和y坐标。为了创建一个480*120像素的画布,并在其中心绘制一个点,显然这个点的坐标应该为(240,60),我们需要输入如下代码:
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
point(240, 60);
}
效果如图3-1.
图3-1
试着写一个程序,在画布的四个角和中心各画一个像素点。然后试试将点一个一个排列起来的方式绘制水平、竖直或对角线。
基本图形
p5.js包含一系列的函数用来绘制基本形状(如图3-2)。通过将简单的形状(比如线)进行组合就能够创造更多复杂的形状(比如叶子、脸)。
图3-2
为了画一个线段,我们需要四个参数:两个是起始点的坐标,两个是结束点的坐标。
例子3-3:画一条线段
为了画一条从(20,50)开始到(420,110)结束的线段,我们需要输入如下代码:
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
line(20, 50, 420, 110);
}
效果如图3-3。
图3-3
例子3-4:画基本的形状
下面这个图案(图3-4),一个三角形需要六个参数而一个四边形需要八个参数,代码如下:
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
quad(158, 55, 199, 14, 392, 66, 351, 107);
triangle(347, 54, 392, 9, 392, 66);
triangle(158, 55, 290, 91, 290, 112);
}
图3-4
例子3-5:画一个矩形
矩形和椭圆都有四个参数:其中第一个参数和第二个参数是其固定点(对于矩形来说就是左上角那个顶点)的x和y坐标,第三个和第四个参数为图形的宽和高。下面的代码使用 rect() 来绘制一个左上角顶点位置在(180,60),宽和高分别为220和40的矩形(图3-5):
图3-5
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
rect(180, 60, 220, 40);
}
例子3-6:画一个椭圆
对于矩形,x和y的坐标是指的是矩形左上角的顶点坐标,但是对于椭圆则是它中心的坐标。在这个例子中,你会注意到第一个椭圆的中心的y坐标已经在画布外面了(图3-6)。所以你现在知道了,物体是可以部分或全部被画在画布外面的,程序不会报错的。
图3-6
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
ellipse(278, -100, 400, 400);
ellipse(120, 100, 110, 110);
ellipse(412, 60, 18, 18);
}
p5.js 还没有独立的函数用来画正方形和圆。你可以通过使用给 ellipse() 和 rect() 函数相同的宽和高值来绘制这些图形。
例子3-7:画一个椭圆的一部分
使用 arc() ,你可以画一个椭圆的一部分,代码如下:
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
arc(90, 60, 80, 80, 0, HALF_PI);
arc(190, 60, 80, 80, 0, PI+HALF_PI);
arc(290, 60, 80, 80, PI, TWO_PI+HALF_PI);
arc(390, 60, 80, 80, QUARTER_PI, PI+QUARTER_PI);
}
arc() 函数的前两个参数确定椭圆中心位置,而后两个参数确定椭圆的宽和高。第五个参数设定弧形开始的角度而第六个参数设定弧形结束的角度。这里面的角度使用的单位是弧度制而不是角度制。弧度制是基于圆周率pi(3.14159)来测量角的值。图3-7显示了两者的关系。在这个例子中,有四个弧度值使用的非常频繁,所以我们将四个弧度值作为常量添加到p5.js库中去。这四个值分别为 PI, QUARTER_PI,HALF_PI 和 TWO_PI,你可以在程序中使用它们来替代 180°,45°,90°,360°。
图3-7
例子3-8:用角度画图
如果你更喜欢使用角度制画图,你可以使用 radians() 函数将角度值转化为弧度值。这个函数接收一个角度参数并把它转化为相应的弧度值。下面的例子的结果与例子3-7一样,不过它使用的是 radians() 函数并用角度值来确定弧的起始点与结束点的值,代码如下:
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
arc(90, 60, 80, 80, 0, radians(90));
arc(190, 60, 80, 80, 0, radians(270));
arc(290, 60, 80, 80, radians(180), radians(450));
arc(390, 60, 80, 80, radians(45), radians(225));
}
例子3-9:使用角度制模式
当然,你可以使用 angleMode() 函数设定在整个项目中都使用角度制还是弧度值。这个函数可以让你的项目中的所有函数的输入值和返回值都是以角度值的形式或者都是以弧度值的形式,而不用你自己去转换。下面的例子与例子3-8的结果相同,但是它使用了 angleMode(DEGREES) 函数将弧的起始点与结束点的值都使用角度表示:
function setup() {
createCanvas(480, 120);
angleMode(DEGREES);
}
function draw() {
background(204);
arc(90, 60, 80, 80, 0, 90);
arc(190, 60, 80, 80, 0, 270);
arc(290, 60, 80, 80, 180, 450);
arc(390, 60, 80, 80, 45, 225);
}
绘制的顺序
当一个程序运行时,电脑从程序最上面开始,一行一行读取,直到程序的最后一行。如果你想让一个图形在其他所有图形上面显示,你需要将绘制这个图形的代码放在其他所有图形的后面。
例子3-10:控制你绘制的顺序
如图3-8。
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
ellipse(140, 0, 190, 190);
// The rectangle draws on top of the ellipse
// because it comes after in the code
rect(160, 30, 260, 20);
}
图3-8
例子3-11:将顺序反过来
将3-10中的 rect() 和 ellipse() 函数顺序颠倒一下, 你会发现现在圆在矩形的上面了(如图3-9)。
图3-9
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
rect(160, 30, 260, 20);
// The ellipse draws on top of the rectangle
// because it comes after in the code
ellipse(140, 0, 190, 190);
}
你可以把这个想成是在用刷子画画或者是在做拼版画。你添加的最后一个元素总是在最上面。
设定图形的属性
现在,你可能希望能控制更多图形的属性,而不是只能修改位置和大小。为此,你需要一系列的函数来设定这些属性。
例子3-12:设定线条的宽度
线条默认的宽度是一个像素,不过你可以通过 strokeWeight() 函数来改变它(如图3-10)。这个函数只有一个参数,就是你要设定的线宽值,代码如下:
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
ellipse(75, 60, 90, 90);
strokeWeight(8); // Stroke weight to 8 pixels
ellipse(175, 60, 90, 90);
ellipse(279, 60, 90, 90);
strokeWeight(20); // Stroke weight to 20 pixels
ellipse(389, 60, 90, 90);
}
图3-10
例子3-13:设定线条的属性
strokeJoin() 函数可以设定线条连接的方式,而 strokeCap() 函数用来设定线段两个端点的样式(如图3-11):
图3-11
function setup() {
createCanvas(480, 120);
strokeWeight(12);
}
function draw() {
background(204);
strokeJoin(ROUND); // Round the stroke corners
rect(40, 25, 70, 70);
strokeJoin(BEVEL); // Bevel the stroke corners
rect(140, 25, 70, 70);
strokeCap(SQUARE); // Square the line endings
line(270, 25, 340, 95);
strokeCap(ROUND); // Round the line endings
line(350, 25, 420, 95);
}
rect() 函数 和 ellipse() 函数位置的确定方式是通过 rectMode() 函数和 ellipseMode() 函数确定的。 通过查阅p5.js参考文档,你可以看到如何利用矩形的中心点放置矩形(而不是通过左上角的顶点)或者如何像矩形一样使用椭圆左上角的位置来放置椭圆。
当这些属性被确定以后,之后所画的所有图形都将受到影响。 比如,在例子3-12中,你会发现第二个圆和第三个圆具有着相同的轮廓宽度,尽管线宽在之前只被设置了一次。
在本例中, strokeWeight(12) 出现在 setup() 函数中而不是 draw() 函数中。这是因为我们希望对于所有的图形采用同样的设置,所以我们将其放在 setup() 函数中,这样就仅仅需要设置一次。当然,这样写是为了程序结构更清晰虽然将其放入draw() 函数中也将会得到相同的视觉效果。
颜色
在此之前的示例中,所有的图形都是使用黑色的轮廓并填充为白色。如果你想让图形变得更多彩,你需要使用到 fill() 和 stroke() 函数。 这两个函数的参数范围是 0 到 255,其中255代表白色, 128 代表中灰,而 0 代表黑色。 图3-12展示了参数值从 0 到 255 所代表的不同灰度。 我们之前例子中使用的 background() 函数就是用来设置画布的背景色的,而不是用来设置轮廓色或者填充色的。
图3-12
例子3-14 : 画出不同的灰色
这个例子展示了在黑色背景上三种不同的灰色值(如图3-13):
图3-13
function setup() {
createCanvas(480, 120);
}
function draw() {
background(0); // Black
fill(204); // Light gray
ellipse(132, 82, 200, 200); // Light gray circle
fill(153); // Medium gray
ellipse(228, -16, 200, 200); // Medium gray circle
fill(102); // Dark gray
ellipse(268, 118, 200, 200); // Dark gray circle
}
例子3-15:控制填充和轮廓
你可以使用 noStroke() 函数来取消图形的轮廓,也可以使用 noFill() 函数取消对一个图形进行颜色的填充(如图3-14):
图3-14
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
fill(153); // Medium gray
ellipse(132, 82, 200, 200); // Gray circle
noFill(); // Turn off fill
ellipse(228, -16, 200, 200); // Outline circle
noStroke(); // Turn off stroke
ellipse(268, 118, 200, 200); // Doesn’t draw!
}
这里需要注意的是你不能同时取消填充和轮廓,这将会导致画布上什么都不会出现。
例子3-16:为你的图形加上颜色
你可以使用三个参数来混合成一个颜色,这三个参数分别表示红色、绿色和蓝色。因为这本书是黑白打印的,所以你只能看到灰色(然而我们是电子版的,哈哈)。运行下面的代码,让颜色展示出来吧(图3-15):
图3-15
function setup() {
createCanvas(480, 120);
noStroke();
}
function draw() {
background(0, 26, 51); // Dark blue color
fill(255, 0, 0); // Red color
ellipse(132, 82, 200, 200); // Red circle
fill(0, 255, 0); // Green color
ellipse(228, -16, 200, 200); // Green circle
fill(0, 0, 255); // Blue color
ellipse(268, 118, 200, 200); // Blue circle
}
这个例子里的颜色采用的是RGB格式的,电脑就是用这个格式来定义在屏幕上输出的颜色的。三个数值分别表示红、绿和蓝,而这三个值的范围都是0到255,不同的值代表不同的灰度。background() 、 fill() 、 stroke() 函数中参数就是这三个值。
例子3-17:设置透明度
在 fill() 或者 stroke() 函数中,通过增加第四个可选择的参数,你可以控制颜色的透明度。第四个参数被称为 alpha值,当然它的范围也是 0 到 255 。 0 代表颜色完全透明(颜色将不显示在屏幕上),而255代表完全不透明,而 0 到 255 之间的值可以让颜色在屏幕上混合(如图3-16):
图3-16
function setup() {
createCanvas(480, 120);
noStroke();
}
function draw() {
background(204, 226, 225); // Light blue color
fill(255, 0, 0, 160); // Red color
ellipse(132, 82, 200, 200); // Red circle
fill(0, 255, 0, 160); // Green color
ellipse(228, -16, 200, 200); // Green circle
fill(0, 0, 255, 160); // Blue color
ellipse(268, 118, 200, 200); // Blue circle
}
自定义绘图
使用p5.js,你不会被限制只能绘制这些基本的几何图形,你也可以通过一系列点的连接绘制新的图形。
例子3-18:画一个箭头
beginShape() 表示一个新的图形绘制的开始。vertex() 函数用来定义新图形的每个顶点的坐标(x,y)。最后, endShape() 表示新图形绘制的结束(图3-17):
图3-17
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
beginShape();
vertex(180, 82);
vertex(207, 36);
vertex(214, 63);
vertex(407, 11);
vertex(412, 30);
vertex(219, 82);
vertex(226, 109);
endShape();
}
例子3-19:关闭那个缺口
当你运行例子3-18时,你会发现第一个点与最后一个点没有连接。你可以使用 CLOSE作为 endShape() 函数的参数将其连接在一起(如图3-18):
图3-18
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
beginShape();
vertex(180, 82);
vertex(207, 36);
vertex(214, 63);
vertex(407, 11);
vertex(412, 30);
vertex(219, 82);
vertex(226, 109);
endShape(CLOSE);
}
例子3-20:创作一些动物
使用 vertex() 函数,我们可以绘制很多轮廓复杂的图形。p5.js一次可以绘制成千上万条线条,它可以绘制任何你能想象的图形。请看下面的例子(图3-19):
图3-19
function setup() {
createCanvas(480, 120);
}
function draw() {
background(204);
// Left creature
beginShape();
vertex(50, 120);
vertex(100, 90);
vertex(110, 60);
vertex(80, 20);
vertex(210, 60);
vertex(160, 80);
vertex(200, 90);
vertex(140, 100);
vertex(130, 120);
endShape();
fill(0);
ellipse(155, 60, 8, 8);
// Right creature
fill(255);
beginShape();
vertex(370, 120);
vertex(360, 90);
vertex(290, 80);
vertex(340, 70);
vertex(280, 50);
vertex(420, 10);
vertex(390, 50);
vertex(410, 90);
vertex(460, 120);
endShape();
fill(0);
ellipse(345, 50, 10, 10);
}
程序的注释
这一章的示例在每行代码的最后均使用了双斜线(//)为其添加了注释。注释是程序的一部分,但是在程序运行时会被忽略。它们非常有用,可以时刻提醒你这段代码的功能。如果其他人阅读你的代码,注释可以很好地帮助他们理解你的想法。
当你要在大量选项中进行选择时,注释也是非常有帮助的。当你试着为一个椭圆选取一个合适的颜色:
function setup() {
createCanvas(200, 200);
}
function draw() {
background(204);
fill(165, 57, 57);
ellipse(100, 100, 80, 80);
}
现在假设我想尝试另外一种红色,但我我并不想丢弃之前的颜色。我可以复制粘贴这一行,修改一下,并把之前的那行注释掉:
function setup() {
createCanvas(200, 200);
}
function draw() {
background(204);
//fill(165, 57, 57);
fill(144, 39, 39);
ellipse(100, 100, 80, 80);
}
将双斜线(//)放置在一行代码的行首可以暂时使它失效。当我再想使用这一行时,我可以移除 // 然后将它放置在其他行:
function setup() {
createCanvas(200, 200);
}
function draw() {
background(204);
fill(165, 57, 57);
//fill(144, 39, 39);
ellipse(100, 100, 80, 80);
}
当你使用p5.js时,你将会发现你创造了大量的想法,使用注释来标记或者禁止运行代码将会帮助你保持多个选择路径。
绘制机器人 1
图3-20
图3-20里是一个机器人,它的名字叫P5。在这本书里,我们将会有十种不同的程序来绘制它。而每一种绘制方法都包含了不同的编程思想。P5机器人的设计灵感来源于斯坦福研究所(Stanford Research Institute)设计的Sputnik I(1957)、David Lynch指导的《星际奇兵》 (1984)中的无人战斗机以及《2001太空漫游》(1968)中的HAL 9000等众多令人喜爱的机器人形象。(译者注:很抱歉,我想吐个槽都不知如何吐)
第一个绘制机器人的程序使用的是本章之前提到的那些绘图函数。 fill() 和 stroke() 函数用来设置灰度值。 line() 、ellipse() 和 rect() 函数定义机器人的脖子、天线、身体和头的形状。为了能够更好地熟悉这些函数,你可以运行下面的程序并修改参数重新设计机器人。
function setup() {
createCanvas(720, 480);
strokeWeight(2);
ellipseMode(RADIUS);
}
function draw() {
background(204);
// Neck
stroke(102); // Set stroke to gray
line(266, 257, 266, 162); // Left
line(276, 257, 276, 162); // Middle
line(286, 257, 286, 162); // Right
// Antennae
line(276, 155, 246, 112); // Small
line(276, 155, 306, 56); // Tall
line(276, 155, 342, 170); // Medium
// Body
noStroke(); // Disable stroke
fill(102); // Set fill to gray
ellipse(264, 377, 33, 33); // Antigravity orb
fill(0); // Set fill to black
rect(219, 257, 90, 120); // Main body
fill(102); // Set fill to gray
rect(219, 274, 90, 6); // Gray stripe
// Head
fill(0); // Set fill to black
ellipse(276, 155, 45, 45); // Head
fill(255); // Set fill to white
ellipse(288, 150, 14, 14); // Large eye
fill(0); // Set fill to black
ellipse(288, 150, 3, 3); // Pupil
fill(153); // Set fill to light gray
ellipse(263, 148, 5, 5); // Small eye 1
ellipse(296, 130, 4, 4); // Small eye 2
ellipse(305, 162, 3, 3); // Small eye 3
}
(译者:Jason Lee,邮箱:676574039@qq.com)