{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "title: p5.js教程02-绘制简单图形\n", "date: 2018-06-06 20:17:55\n", "tags: [p5.js]\n", "toc: true\n", "xiongzhang: true\n", "\n", "\n", "---\n", "\n", "\n", "\n", "> 声明: 本文由[DataScience](http://mlln.cn)发表,未经允许不得转载。 转载请注明[本文链接](http://mlln.cn)mlln.cn, 并在文后留言`转载`.\n", "\n", "本文代码运行环境:\n", "\n", "- windows10\n", "- jupyter notebook\n", "- p5.js 0.6.1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在计算机屏幕上绘图就像在方格纸上绘图一样。但随着新概念的引入,用软件绘制简单的形状扩展到绘制动画和界面。在我们进行高阶应用之前,我们需要从头开始。计算机屏幕是像素构成的网格。像素的坐标就是在屏幕上的位置。创建p5.js程序时,可以使用Web浏览器进行预览。在浏览器的窗口中,p5.js创建一个画布,即绘制图形的区域。画布可以与窗口大小相同,也可以具有不同的尺寸。画布通常位于窗口的左上角,但您可以将其放置在其他位置。在画布上绘制时,x坐标是距画布左边缘的距离,y坐标是距上边缘的距离。我们写这样一个像素的坐标:(x,y)。因此,如果画布是200×200像素,左上角是(0,0),中间是(100,100),右下角是(199,199)。这些数字可能看起来令人困惑;为什么我们从0到199而不是1到200?答案是,在代码中,我们通常从0开始计算,这使得很多程序写起来更简单。\n", "\n", "(下面我们注册p5魔法, 以便于在jupyter notebook中使用p5.js, 如果你不用notebook, 请忽略这部分python代码)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "from IPython.core.magic import register_cell_magic\n", "from IPython.display import IFrame\n", "\n", "TEMPLATE = \"\"\"\n", "\n", "\n", "\n", "\n", "\n", "Notebook中显示P5.js页面\n", "\n", "\n", "
p5.js效果展示: %(name)s
\n", "\n", "\n", "\n", "\"\"\"\n", "\n", "\n", "@register_cell_magic\n", "def p5(line, cell):\n", " file_id, kws = line.split(' ')[0], line.split(' ')[1:]\n", " kwargs = {}\n", " for kw in kws:\n", " k, v = kw.split('=')\n", " kwargs[k] = v\n", " filename = f\"p5js-html/p5-02-{file_id}.html\"\n", " with open(filename, \"w\") as fp:\n", " fp.write(TEMPLATE % {\"script\": cell, \"name\": filename})\n", " if 'height' not in kwargs:\n", " kwargs['height'] = '200px'\n", " return IFrame(filename, width=\"100%\", **kwargs)\n", "\n", "del p5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 创建画布\n", "\n", "创建画布并通过函数在内部绘制图像。函数是p5.js程序的基本构建块。函数的行为由其参数定义。例如,几乎每个p5.js程序都有一个createCanvas函数,用于创建具有特定宽度和高度的绘图画布。如果您的程序没有createCanvas函数,则p5会默认的创建尺寸为100×100像素的画布。\n", "\n", "createCanvas函数有两个参数;第一个设置绘图画布的宽度,第二个设置高度。要绘制宽度为80像素,高度为60像素的画布,请键入:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 create-canvas height=100px\n", "\n", "function setup() {\n", " createCanvas(80, 60); \n", " // 设置背景以便于你看清画布\n", " background(100)\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 绘制基本图形\n", "\n", "#### 像素点\n", "\n", "要设置画布中单个像素的颜色,我们使用point()函数。它有两个定义位置的参数:x坐标后跟y坐标。要在它的中心创建一个小画布和一个点,坐标(24,6),键入:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 draw-point height=100px\n", "\n", "function setup() { createCanvas(48, 24); }\n", "function draw() { \n", " background(204) \n", " point(24, 6)\n", "} " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "你可能看不清这个像素点。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 线段\n", "\n", "要在坐标(20,50)和(420,110)之间画一条线,请尝试:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 draw-line height=150px\n", "\n", "function setup() { createCanvas(480, 120); }\n", "function draw() { \n", " background(204); \n", " line(20, 50, 420, 110); \n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 图形\n", "\n", "遵循上例的模式,三角形需要六个参数,四边形需要八个(每个点一对):" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 draw-shape height=180px\n", "\n", "function setup() { createCanvas(480, 120); }\n", "function draw() { \n", " background(204); \n", " // 四边形\n", " quad(118, 55, 199, 14, 392, 66, 351, 107); \n", " // 三角形\n", " triangle(247, 54, 392, 9, 392, 66); \n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 矩形和圆形\n", "\n", "矩形和椭圆都用四个参数定义:第一个和第二个是锚点的x和y坐标,第三个是宽度,第四个是高度。" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 draw-rect-ellipse height=180px\n", "\n", "function setup() { createCanvas(480, 120); }\n", "function draw() { \n", " background(204); \n", " // 矩形\n", " rect(10,10,100,100)\n", " // 圆形\n", " ellipse(150,60,80,100)\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 绘制扇形\n", "\n", "arc可以绘制圆形的一部分。\n", "\n", "第一个和第二个参数设置位置,而第三个和第四个参数设置宽度和高度。第五个参数设置开始弧的角度,第六个参数设置要停止的角度。角度以弧度而不是度数设置。弧度是基于pi值(3.14159)的角度测量值。图3-2显示了两者的关系。如本例中所示,使用了四个弧度值,因此它们的特殊名称被添加为p5.js的一部分。值PI,QUARTER_PI,HALF_PI和TWO_PI可用于替换180°,45°,90°和360°的弧度值。" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 draw-arc height=180px\n", "let x = 0\n", "function setup() { createCanvas(580, 120); }\n", "function draw() { \n", " background(204); \n", " x += 0.1\n", " arc(90, 60, 80, 80, 0, HALF_PI); \n", " arc(190, 60, 80, 80, 0, PI+HALF_PI); \n", " arc(290, 60, 80, 80, PI, TWO_PI+HALF_PI); \n", " // 动态改变起始位置, 可以看到转动效果\n", " arc(390, 60, 80, 80, QUARTER_PI+x, PI+QUARTER_PI+x); \n", " // 如果你想使用度数, 而不是弧度\n", " arc(490, 60, 80, 80, radians(30), radians(90))\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 图形重叠次序\n", "\n", "先后绘制的图形有可能覆盖之前绘制的图形。" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 draw-overlap height=180px\n", "\n", "function setup() { createCanvas(480, 120); }\n", "function draw() { \n", " background(204); \n", " // 矩形\n", " rect(10,10,100,100)\n", " // 圆形\n", " ellipse(100,60,80,100)\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 图形属性\n", "\n", "#### 轮廓粗细(strokeWeight)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 stroke-properties height=180px\n", "\n", "function setup() { createCanvas(480, 120); }\n", "let weight = 1\n", "function draw() { \n", " weight += 1\n", " background(204); \n", " // 设置线粗细\n", " strokeWeight(weight)\n", " // 圆形\n", " ellipse(100,60,80,100)\n", " if (weight==200){\n", " weight=0\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 轮廓端点风格" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 strokeJoin height=180px\n", "\n", "function setup() { createCanvas(480, 120); strokeWeight(12); }\n", "function draw() {\n", " background(204); \n", " // 圆弧夹角\n", " strokeJoin(ROUND); \n", " rect(40, 25, 70, 70); \n", " // Bevel型夹角\n", " strokeJoin(BEVEL); \n", " rect(140, 25, 70, 70); \n", " // 直角线端点\n", " strokeCap(SQUARE); \n", " line(270, 25, 340, 95); \n", " // 圆弧形端点\n", " strokeCap(ROUND); \n", " line(350, 25, 420, 95); \n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 颜色\n", "\n", "到目前为止,所有的形状都是白色的,带有黑色轮廓。要更改此设置,请使用fill()和stroke()函数。参数值的范围为0到255,其中255为白色,128为中灰色,0为黑色。我们在前面的例子中看到的background()函数以相同的方式工作,它设置画布的背景颜色。" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 color height=180px\n", "\n", "function setup() { createCanvas(480, 120) }\n", "function draw() {\n", " background(204); \n", " // 填充红色\n", " // 三个参数分别是 R G B\n", " fill(255, 0, 0) \n", " // 轮廓黑色\n", " stroke(0,0,0); \n", " rect(40, 25, 70, 70); \n", " // 填充绿色\n", " fill(0, 255, 0) \n", " // 轮廓红色\n", " stroke(255,0,0); \n", " rect(140, 25, 70, 70); \n", " // 轮廓颜色(随便设置了一个颜色)\n", " stroke(129,20,255); \n", " line(270, 25, 340, 95); \n", " // 第4个参数设置透明度\n", " fill(0, 255, 0, 100)\n", " rect(370, 25, 70, 70); \n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 复杂图形\n", "\n", "你不仅可以使用这些基本几何形状 - 还可以通过连接一系列点来定义新形状。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "beginShape()函数指示新形状的开始。 vertex()函数用于定义点的x和y坐标。最后,调用endShape()来表示形状已完成:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 beginshape-endshape height=180px\n", "\n", "function setup() { createCanvas(480, 120) }\n", "function draw() {\n", " background(204); \n", " // 绘制一个太阳\n", " fill(255,0,0)\n", " stroke(255, 255, 0)\n", " strokeWeight(6)\n", " ellipse(360, 110, 100, 100)\n", " // 开始绘制山的图形\n", " // 设置颜色为灰色\n", " fill(45,45,45)\n", " stroke(0, 0, 0)\n", " strokeWeight(1)\n", " beginShape()\n", " vertex(0,120)\n", " vertex(10, 40)\n", " vertex(40, 100)\n", " vertex(80, 20)\n", " vertex(110, 80)\n", " vertex(260, 50)\n", " vertex(360, 110)\n", " vertex(400, 10)\n", " vertex(480, 120)\n", " // 结束绘制图形\n", " endShape()\n", " \n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果你觉得不过瘾, 可以做一个太阳落山的动画:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%p5 beginshape-endshape-moving height=180px\n", "let sun_y=0\n", "function setup() { createCanvas(480, 120) }\n", "function draw() {\n", " sun_y += 1\n", " if(sun_y==200){\n", " sun_y=1\n", " }\n", " background(204); \n", " // 绘制一个太阳\n", " fill(255,0,0)\n", " stroke(255, 255, 0)\n", " strokeWeight(6)\n", " ellipse(360, sun_y, 100, 100)\n", " // 开始绘制山的图形\n", " // 设置颜色为灰色\n", " fill(45,45,45)\n", " stroke(0, 0, 0)\n", " strokeWeight(1)\n", " beginShape()\n", " vertex(0,120)\n", " vertex(10, 40)\n", " vertex(40, 100)\n", " vertex(80, 20)\n", " vertex(110, 80)\n", " vertex(260, 50)\n", " vertex(360, 110)\n", " vertex(400, 10)\n", " vertex(480, 120)\n", " // 结束绘制图形\n", " endShape()\n", "}" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4" } }, "nbformat": 4, "nbformat_minor": 2 }