闪电编程课——第三课

138 阅读27分钟

第三课:P5.js创意编程入门

闪电编程,助你闪电入门!⚡️⚡️⚡️

本节课的大纲:

  • javascript语言关键概念回顾
  • 本地编程环境配置
  • p5.js基本代码结构
  • 元素:坐标、形状、颜色
  • 动画:旋转、平移、鼠标事件
  • 用p5.js做的第一个CodeArt作品

本节课学完后能收获什么:

  • 安装好本地环境并用其创建p5.js项目
  • 掌握最基本的p5.js “武器库”:形状、颜色、动画
  • 建立“坐标”和“函数”的概念并实操。
  • 完成一幅简单的数字艺术作品。

课前准备:

课前复习:JavaScript语言关键概念

抽查:变量 函数、参数如何定义? 如何使用?

参考: 闪电编程入门课-第二课

第一部分、搭建你的创意画板:p5.js环境配置

在开始创作之前,我们需要准备好我们的“画板”和“画笔”。p5.js 提供了两种非常便捷的开发方式,你可以根据自己的喜好选择其一。

方案一:在线画板 —— p5.js Web Editor (打开即用)

这是最简单、最快速的入门方式,无需在你的电脑上安装任何软件。

  1. 打开浏览器,访问 p5.js Web Editor
  2. 你会看到一个清晰的界面,主要分为三个部分:左侧是代码编辑器,你将在这里编写代码;右侧是预览窗口,实时展示你的代码生成的可视化结果;底部是控制台,用于显示信息或错误。
  3. 你可以直接在编辑器中开始编写代码,点击顶部的“Play”按钮即可运行。

p5.js Web Editor界面

方案二:本地画室 —— Trae + p5.js

为什么需要本地开发环境?

在线编辑器虽然方便,但有时需要离线工作,或需要使用本地资源(如图片、音频)。

如果要将项目文件保存在本地,并使用更专业的代码编辑器,VSCode是绝大多数程序员的选择。

但在AI时代,我们推荐使用 Trae,由字节跳动出品,一个免费的带AI助手的VSCode。

环境配置步骤(带大家操作)

  1. 安装 Trae:前往 Trae CN 官网 下载并安装。 国际版地址:TRAE - The Real AI Engineer

image.png 下载完成后,打开Trae:

image.png

  1. 安装 Live Server 插件:在 Trae的扩展商店中搜索并安装 "Live Server" 插件。它能帮你创建一个本地服务器,并实现代码保存后浏览器自动刷新。

image.png

  1. 创建项目文件

    • 创建一个项目文件夹,例如 p5-art
    • 在文件夹中创建三个文件:
      • index.html,
      • style.css
      • sketch.js

image.png

动手练习:搭建你的本地“数字画室”

让我们通过本地方式,把画室搭建起来。

练习目标:  创建一个能运行p5.js代码的基本HTML页面。

步骤:

  1. 编写 index.html 文件:

    将以下代码复制到你的 index.html 文件中。

  <!DOCTYPE html>
  <html lang="en">
    <head>
      <!-- 引入 p5.js 核心库 -->
      <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.js"></script>
      <!-- (可选) 引入 p5.js 音频库 -->
      <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/addons/p5.sound.min.js"></script> -->
      <meta charset="utf-8" />
    </head>
    <body>
      <main>
      </main>
      <!-- 引入你自己的 sketch 文件 -->
      <script src="sketch.js"></script>
    </body>
  </html>

代码解读:

HTML代码用于搭建并呈现我们网页的基本框架结构,这段HTML代码做了两件重要的事:

  • 通过 <script> 标签从一个公共CDN(内容分发网络)地址加载了p5.js的核心库。这意味着我们的“画板工具箱”已经准备好了。——引入资源
  • 加载了我们自己的 sketch.js 文件,这是我们即将开始创作的地方。——写核心p5代码的地方。
  1. 编写 style.css 文件(可选):

    将以下代码复制到 style.css,它可以让我们的画布居中显示,看起来更舒服。

     body {
      margin: 0;
      padding: 0;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      background-color: #222; /* 给页面一个深色背景,突出画布 */
    }
    
  2. 编写 sketch.js 文件(初始代码):

    现在,在 sketch.js 文件里写下我们第一行“魔法咒语”。

function setup() {
  createCanvas(400, 400); // 创建一个400x400像素的画布
}

function draw() {
  background(220); // 设置背景颜色
  text("你好! P5.js !",100,100)
}
  1. 运行!:在 index.html 文件上右键,选择 "Open with Live Server",浏览器就会自动打开并运行你的作品。

image.png

  1. 修改js代码后,按ctrl+s保存,浏览器会自动刷新页面。

image.png

第二部分、p5.js 的魔法咒语:基本结构

p5.js 是个 JavaScript 的创意编程程式库,其专注在让编程更易于使用及更加广泛的包容艺术家、设计师、教育家、初学者以及任何人!

小白解释版:

JavaScript是一门编程语言,p5.js就是在这门语言上的咒语库(多种法术张口就用,力量强大。Harry Potter里的索命咒: 阿瓦达啃大瓜!)

可以点击p5.js 官网寻找更多信息

2.1 认识代码结构

来看一个最基础的p5.js代码结构:

image.png

每个 p5.js 项目(我们称之为 "sketch")都会有两个核心函数进行构建:setup()draw()。你可以把它们想象成一位画家创作的两个阶段。

setup()函数

  • setup() 函数就像是画画前的准备工作:把画布置于画架,挤好颜料,选好画笔。这个过程只发生一次

draw()函数

  • draw() 函数则是持续不断的绘画动作:一笔一笔地将颜料涂上画布。这个过程会循环往复,默认每秒重复约60次,从而形成动画。

程序执行流程

  1. 首先执行setup()函数一次
  2. 然后持续执行draw()函数内的代码,直到程序停止。
示例练习:颜色变换小动画

我们在 setup 函数中创建“画布”,在 draw 函数中让画布“动画化”。

将下面的代码复制到js文件中:

function setup() {
  createCanvas(200, 200); // <- 创建画布
}

function draw() {
  background(220); // <- 设置背景色
}

上面的代码创建了一个 200x200 的画布,背景色为灰色。呈现效果:

image.png

你可能会奇怪,我们明明添加了 draw 函数,并且它会每秒画60次,为什么画布没有任何动画?

好问题!因为我们的 draw 函数每一次重绘后都是相同的,它始终将背景色设置为 220 这个灰阶值(后文会介绍颜色)。既然每一次都是相同的,那么我们也就看不出任何动画过程了。

修改: 改变draw 函数内的代码,让背景色随着帧数量而变化:

function draw() {
  background(frameCount % 255); // <- 随着帧数量而变化灰阶值
  text("帧数: " + frameCount, 100, 100);   //显示当前帧数
}

观察: 出现了什么样的效果?

  1. 动态变化的关键:frameCount % 255

    • frameCount:系统内置的变量,代表程序从运行开始时到当前的 “总帧数”(默认每秒 60 帧,随时间递增)。
    • %(取模运算符):计算 frameCount 除以 255 后的余数。由于余数范围始终是 0-254(当 frameCount 为 255 时,余数为 0;256 时余数为 1,以此类推),相当于把 “持续递增的帧数” 限定在 0-254 的区间内。
  2. 最终效果
    随着程序运行,background() 的参数会从 0(黑)→1→2→…→254(接近白)→0(黑)循环变化,画布背景会呈现 “从黑逐渐变浅、到接近白后瞬间回到黑、再重新变浅” 的周期性灰阶渐变效果

2.2 引入文本和图片

文本

创建文本的方法叫 text()

语法如下:

text(str, x, y)
  • str: 文本
  • xy 是文本基线左侧的坐标

使用 textSize() 方法可以修改文字的字号

textSize(100)
text('雷猴', 20, 100)

image.png

图片

一般把图片和视频等资源加载写在 preload() 里。

  • 我们使用 loadImage() 方法加载图片。——(从网络链接或本地拿过来)

  • 使用 image() 方法渲染图片。——(渲染到画面上)

注意:加载和渲染是分开2步操作的!

示例代码

let img = null

function preload() {
    img = loadImage('https://p3-passport.byteimg.com/img/user-avatar/848d36b5efa8e055ab080bff4f27e669~180x180.awebp') // 加载图片
}

function setup() {
    createCanvas(180, 180) // 创建画布
}

function draw() {
    image(img, 0, 0) // 渲染图片
}

2.3 p5.js的注释

在代码中添加注释是一个好习惯,注释是对你接下来这部分代码的功能说明和解释。

在p5.js中,使用//来添加单行注释,

使用/* */来添加多行注释。

// 这是一个单行注释

/*
这是一个多行注释
可以跨越多行
*/

动手练习 :改变世界

尝试修改你的代码,

  1. 在setup()中,使用createCanvas()函数,将画布变得更像一个宽屏电影(600x400像素)
  2. 在draw()中,使用 text()函数 写下: “你好,我的第一个p5世界!”. 文字大小设置为50.
  3. 修改background()函数,把背景色从灰色变成你喜欢的任何颜色(例如,天空蓝)。
  4. 尝试: 修改createCanvas()函数的参数,创建一个铺满你浏览器窗口的画布。p5.js为此提供了两个便捷的内置变量:windowWidth 和 windowHeight

示例代码

    function setup() {
      // 练习:将画布尺寸改为 600 宽, 400 高
      createCanvas(600, 400);
      // createCanvas(windowWidth, windowHeight);
    }

    function draw() {
      // 练习:使用 RGB 值将背景设置为天蓝色
      // 提示:天蓝色的 RGB 值大约是 (135, 206, 235)
      background(135, 206, 235);
      
      textSize(50)
      text('你好,我的第一个p5世界!', 20, 100)
    }

三、p5.js的基本形状和颜色

P5.js的坐标系统

在 P5.js 中画画,首先要懂 “画布上的位置”—— 坐标系统。

类比:就像手机屏幕,左上角是 “起点”(0,0),向右 x 坐标变大,向下 y 坐标变大(和数学坐标系不同,记好这个区别!)。​

示例:​

  • 画布左上角:(0, 0);​
  • 400x400 画布的中心:(200, 200);​
  • 画布右下角:(400, 400)。

座标系

左侧的是笛卡尔座标系,0,0 位于中间。而右图计算机图形学中的座标系 0,0 位于左上角。所以你会发现我们上面的代码中,并没有负数座标值。

3.1 常用基本图形​

P5.js 提供了现成的 “画笔工具”,能直接画点、线、矩形、圆形等,核心函数如下:​

图形​函数语法​说明​
点​point(x, y)​(x,y) 是点的坐标​
线​line(x1,y1, x2,y2)​从 (x1,y1) 到 (x2,y2) 的直线​
矩形​rect(x,y, w,h)​左上角 (x,y),宽 w,高 h​
圆形 / 椭圆​ellipse(x,y, w,h)​中心 (x,y),宽 w,高 h(w=h 时是圆形)​
三角形​triangle(x1,y1, x2,y2, x3,y3)​三个顶点的坐标​
弧线​arc(x,y, w,h, start, end)​中心 (x,y),宽 w 高 h,从 start 到 end 弧度​

练习

在练习区里面试试自己画矩形、椭圆、线和点,代码如下:

    function setup() {
      createCanvas(200, 200);
    }

    function draw() {
      point(81, 70); // <- 绘制一个点,座标 81, 70
      
      line(90, 150, 80, 160); // <- 绘制一条线,起点座标 90, 150,终点座标 80, 160
      
      rect(100, 100, 20, 100); // <- 绘制一个矩形,座标 100, 100,宽 20,高 100
      
      ellipse(100, 70, 60, 60); // <- 绘制一个椭圆,座标 100, 70,宽 60,高 60
      ellipse(81, 70, 16, 32); // <- 绘制一个椭圆,座标 81, 70,宽 16,高 32
    }

3.2 p5.js的色彩体系

3.2.1 颜色如何控制

设置色彩:

  • background(r, g, b): 设置背景色。
  • fill(r, g, b): 设置图形的填充色。
  • stroke(r, g, b): 设置图形的描边(轮廓)色。

取消:

  • noFill(): 取消填充。
  • noStroke(): 取消描边。
  • strokeWeight(数值):设置边框粗细;

注意: fill()stroke() 的设置会一直生效,直到你再次调用它们来改变颜色。这就像画画时,除非你换一支画笔,否则会一直使用当前画笔的颜色。

案例练习

试试下面的代码会出现什么颜色:

function draw() {
  background(220);
  fill(255, 0, 0);        // 红色填充
  stroke(0, 0, 255);      // 蓝色描边

  strokeWeight(5);        // 描边粗细5像素
  ellipse(200, 200, 100, 100);

  fill(0, 255, 0, 127);   // 绿色+半透明(第4个参数是透明度0~255)
  noStroke();             // 关闭描边
  rect(250, 250, 80, 80);
}
3.2.2 不同颜色模式

p5 提供了多种颜色表示方式,包括 RGB、HSB、CSS颜色名、hex十六进制颜色等。

代码:

颜色默认用 RGB(红、绿、蓝)模式表示,每个值的范围是 0 到 255。

    function setup() {
      createCanvas(260, 100);
    }

    function draw() {
      background(200); // <- 单个数字参数,作为灰阶
      noStroke();    // 无描边

      fill(255, 0, 0); // <- RGB 颜色,红色
      circle(30, 50, 40);
    }

HSB 色相(H)、饱和度(S)、亮度(B)颜色模式:

    colorMode(HSB); // <- 改为 HSB 颜色模式
    fill(50, 55, 100); // <- HSB 颜色,黄色
    circle(80, 50, 40);

其它颜色模式:

   fill("green"); // <- CSS 颜色,绿色
   circle(180, 50, 40);

   fill("#4A58DF"); // <- Hex 颜色,蓝色
   circle(230, 50, 40);

我们首先设置了颜色模式和背景色,然后连续创建了五个不同颜色的圆,它们的颜色使用方法各不相同。

fill 函数调用以后的所有图形都会具有这个颜色,直到下一个 fill 函数调用。这就是为什么我们要先调用 fill 再调用 circle

当 backgroundfill 等函数的颜色参数存在第四个值时,它表示 Alpha,代表透明度。

关于HSB

在 HSB 模式中,颜色由如下三种值组成:

image.png

  • H(hue)代表色相:在0~360°的标准色轮上,色相是按位置度量的。在通常的使用中,色相是由颜色名称标识的,比如红、绿或橙色。黑色和白色无色相。

  • S(saturation)表示饱和度:表示色彩的纯度,为0时为灰色。白、黑和其他灰色色彩都没有饱和度的。在最大饱和度时,每一色相具有最纯的色光。取值范围0~100%。数值越大,颜色中的灰色越少,颜色越鲜艳,呈现一种从灰度到纯色的变化。

  • B(brightness)表示亮度:其作用是控制色彩的明暗变化。它同样使用了 0% 至 100% 的取值范围。数值越小,色彩越暗,越接近于黑色;数值越大,色彩越亮,越接近于白色。

3.2.3 案例:画一幅小风景

结合图形和颜色,画一幅包含太阳、山、树的风景:

function setup() {
  createCanvas(600, 400);
  colorMode(HSB, 360, 100, 100); // 用HSB模式调色
}
function draw() {
  // 背景(从浅蓝到深蓝的渐变效果:每帧改背景色,配合循环产生渐变)
  background(frameCount % 360, 30, 100); 
  // frameCount:当前循环次数,%360让色相在0-360循环
  
  // 太阳(右上角橙色圆形)
  fill(45, 100, 100); // 色相45=橙色
  noStroke();
  ellipse(500, 100, 80, 80);
  
  // 山(两个三角形)
  fill(120, 50, 70); // 色相120=绿色
  triangle(0, 400, 300, 150, 600, 400); // 大山顶点
  triangle(100, 400, 300, 250, 500, 400); // 小山顶点
  
  // 树(矩形树干+圆形树冠)
  // 树干
  fill(30, 80, 40); // 色相30=棕色
  rect(100, 320, 20, 80);
  // 树冠
  fill(120, 80, 60);
  ellipse(110, 300, 60, 60);
  ellipse(80, 280, 60, 60);
  ellipse(140, 280, 60, 60);
}

3.2.4 动手练习:个性化风景

练习目标

修改案例代码,添加1种新图形(如云朵、房子、河流),并调整颜色搭配。

练习提示

  • 云朵:用多个白色椭圆叠加(ellipse());
  • 房子:矩形 + 三角形屋顶(rect()+triangle());
  • 河流:蓝色矩形加圆角(rect()后用rectMode(CENTER)调整锚点);
  • 可切换回 RGB 模式试试不同调色效果。

参考代码(带云朵和房子)

function setup() {
  createCanvas(600, 400);
  colorMode(RGB); // 切换回RGB模式
}
function draw() {
  // 背景(固定天空蓝)
  background(135, 206, 235);
  
  // 云朵(多个白色椭圆)
  fill(255);
  noStroke();
  ellipse(200, 100, 60, 40);
  ellipse(230, 100, 70, 45);
  ellipse(260, 100, 60, 40);
  ellipse(180, 110, 50, 35);
  ellipse(270, 110, 50, 35);
  
  // 太阳
  fill(255, 204, 0);
  ellipse(500, 100, 80, 80);
  
  // 山
  fill(34, 139, 34);
  triangle(0, 400, 300, 150, 600, 400);
  fill(46, 139, 87);
  triangle(100, 400, 300, 250, 500, 400);
  
  // 房子
  // 屋顶
  fill(139, 69, 19);
  triangle(400, 280, 480, 280, 440, 230);
  // 房身
  fill(255, 248, 220);
  rect(400, 280, 80, 100);
  // 窗户
  fill(173, 216, 230);
  rect(415, 300, 20, 20);
  rect(445, 300, 20, 20);
  
  // 树
  fill(139, 69, 19);
  rect(100, 320, 20, 80);
  fill(34, 139, 34);
  ellipse(110, 300, 60, 60);
  ellipse(80, 280, 60, 60);
  ellipse(140, 280, 60, 60);
}

四、 赋予生命:p5.js的动画与交互

4.1 动画的原理:draw()每一帧与变量

什么是动画? 动画的原理是通过快速连续地绘制画面,使人类大脑产生运动的错觉。就像早期的电影和动画一样,每秒展示24-60帧画面,就能让我们看到连续的运动。

p5.js中的动画实现

  • draw()函数每秒执行多次(默认60次)
  • draw()中使用变量(如frameCount)来控制图形的位置、大小或颜色,实现变化效果

如何做到“略有不同”呢?答案是变量

让我们来看一个最简单的动画:一个向右移动的圆。

let x = 0; // 定义一个变量x,表示圆的水平位置

function setup() {
  createCanvas(600, 400);
}

function draw() {
  background(220); // 每帧都重绘背景,清除上一帧的圆
  ellipse(x, 200, 50, 50); // 在(x, 200)位置画圆
  
  x = x + 2; // 关键!每一帧都让x的值增加1
  
  // 当圆移动到画布外时,让它回到起点
  if (x > width) { // 'width'是p5.js内置变量,代表画布宽度
    x = 0;
  }
}

在这个例子里,x 的值在 draw() 的每一次循环中都会改变为x + 2,导致圆的位置也随之改变,从而形成了动画。

4.2 鼠标交互:让作品 “活” 起来

P5.js 自带鼠标相关的变量和函数,能轻松实现 “跟随鼠标”、“点击变色” 等交互效果。

4.2.1 鼠标相关的变量

p5.js 提供了一些非常有用的系统变量,它们会随着程序的运行而自动更新。其中最常用的就是鼠标坐标:

  • mouseX: 当前鼠标指针的水平位置 (x坐标)。
  • mouseY: 当前鼠标指针的垂直位置 (y坐标)。
  • mouseIsPressed: 一个布尔值(truefalse),如果鼠标被按下,它就是 true

因为 draw() 函数在不断循环,我们可以在每一帧都使用 mouseXmouseY 来绘制图形,这样图形的位置就会实时跟随你的鼠标移动。

案例:鼠标跟随的彩色圆圈

function setup() {
  createCanvas(600, 600);
  // 如果我们把背景放在setup里,这样画出的笔迹就不会被清除了。
  // background(220);
}

function draw() {
  background(220);

  fill(random(255), random(255), random(255));
  // 按下鼠标会变色
  if (mouseIsPressed) {
    fill(255, 0, 0);
  }
  
  ellipse(mouseX, mouseY, 50, 50);
}

效果: 当鼠标在画布上移动时,会显示一个彩色的圆形,位置与鼠标位置一致。按住鼠标,圆形变色。

4.2.2 鼠标键盘的事件函数

除了跟踪鼠标位置,我们还可以响应用户的具体操作,比如点击鼠标或按下键盘。在P5.js中也内置了相关的 事件函数 来实现。

常用事件函数:

这些函数会在特定事件发生时自动被p5.js调用,你只需要定义它们即可。

  • mousePressed(): 当鼠标被按下时,这个函数里的代码会执行一次。

  • mouseMoved(): 当鼠标在画布上移动时(无论是否按下)执行。

  • mouseDragged(): 当鼠标按住并拖动时执行。

  • keyPressed(): 当任意键盘按键被按下时执行。

使用方式案例:

let brushColor; // 用一个变量来存储画笔颜色

function setup() {
  createCanvas(windowWidth, windowHeight); // 画布铺满浏览器窗口
  background(0); // 黑色背景
  brushColor = color(255, 0, 0); // 初始画笔颜色为红色
}

function draw() {
}

//鼠标移动时,函数被触发
function mouseMoved() {
    fill(brushColor);
    // 在鼠标位置绘制一个直径为30的圆
    circle(mouseX, mouseY, 30);
}

// 当鼠标被点击时,这个函数会被触发
function mousePressed() {
  // 生成一个随机颜色
  brushColor = color(random(255), random(255), random(255));
}

//按下键盘时,触发
function keyPressed() {
    stroke(0,0,255)
    //是否可以加入if判断,使得当特定的键被按下才会响应?
}

控制动画播放和暂停的两个函数:

  • loop():让draw()函数恢复循环执行,类比 “动画播放按钮”;​
  • noLoop():让draw()函数停止循环执行(只执行 1 次),类比 “动画暂停按钮”;

五、项目练习:创世纪——你的互动星空

现在,是时候将本节课所学的一切融会贯通,创造一个完整的、具有艺术感的互动作品了。

这个项目将模拟一个可以由你亲手创造和互动的宇宙星空。

5.1 项目目标

创造一个名为“创世纪”的互动艺术作品,它具备以下特征:

  1. 动态的宇宙背景:  背景颜色会像深空星云一样缓慢变化。
  2. 随机的初始星辰:  程序开始时,会自动生成一片随机分布的星星。
  3. 追随鼠标的彗星:  一道由光点组成的“彗星”或“星尘”会跟随着你的鼠标轨迹。
  4. 点击创生新星:  每当你在画布上点击鼠标,就会在那个位置“诞生”一颗新的、闪亮的星星。
5.2 步骤分解与代码实现

让我们一步步来构建这个小宇宙。

完整代码结构预览:

let stars = []; // 用一个数组来存储所有星星的信息

function setup() {
  createCanvas(windowWidth, windowHeight);
  // ... 初始化代码 ...
}

function draw() {
  // ... 绘制背景和动画 ...
}

function mousePressed() {
  // ... 处理鼠标点击事件 ...
}

Step 1: 搭建夜空背景

在 draw() 函数中,我们将用 background() 创造一个缓慢变化的背景。我们可以使用 frameCount 这个p5.js内置变量,它记录了程序已经运行的总帧数。

// 在 draw() 函数中
function draw() {
  // 使用 sin 函数创造一个在 0-100 之间平滑变化的蓝色调
  // frameCount * 0.01 控制了颜色变化的速度
  let bgColor = map(sin(frameCount * 0.01), -1, 1, 0, 100);
  background(0, 10, bgColor); 
  
  // ... 后续代码将在这里添加 ...
}

Step 2: 播撒初始星辰

我们需要在程序开始时就创建一些星星。最好的地方是在 setup() 函数中。我们可以用一个 for 循环来完成这件事。为了让星星可以被管理(比如之后让它们闪烁),我们最好用一个数组 stars 来存储它们的位置。

let stars = []; // 在文件顶部定义数组
const starCount = 500; // 定义星星的数量

function setup() {
  createCanvas(windowWidth, windowHeight);
  
  // 在 setup 中生成初始的星星
  for (let i = 0; i < starCount; i++) {
    stars.push({
      x: random(width),   // 在画布宽度范围内随机一个x坐标
      y: random(height),  // 在画布高度范围内随机一个y坐标
      size: random(1, 3),   // 随机大小
      brightness: random(150, 255) // 随机亮度
    });
  }
}

// 然后在 draw() 函数中把它们画出来
function draw() {
  // ... 背景代码 ...
  
  // 遍历星星数组,并绘制每一颗星星
  stroke(255); // 星星用白色描边
  for (let star of stars) {
    strokeWeight(star.size);
    point(star.x, star.y);
  }
  
  // ... 后续代码 ...
}

Step 3: 绘制追随鼠标的彗星

我们利用 mouseXmouseY 以及 pmouseXpmouseY 来创造拖尾效果。

// 在 draw() 函数的末尾添加
function draw() {
  // ... 背景和星星的代码 ...
  
  // 绘制彗星尾巴
  stroke(255, 255, 200, 150); // 淡黄色,半透明
  strokeWeight(4);
  line(mouseX, mouseY, pmouseX, pmouseY); // 连接当前帧和上一帧的鼠标位置
}

Step 4: 点击诞生新星

我们将使用 mousePressed() 事件函数。当用户点击时,我们向 stars 数组中添加一颗新的星星。这颗新星会更大、更亮,像一颗刚刚爆发的超新星。

// 在文件底部添加 mousePressed 函数
function mousePressed() {
  // 当鼠标点击时,在鼠标位置添加一颗明亮的大星星
  stars.push({
    x: mouseX,
    y: mouseY,
    size: random(5, 8), // 更大尺寸
    brightness: 255   // 最亮
  });
}
5.3 完整参考代码

将所有部分整合起来,你的 sketch.js 应该是这样的:

// sketch.js - 互动星空项目

let stars = [];
const starCount = 500;

function setup() {
  createCanvas(windowWidth, windowHeight);
  
  // 生成初始的静态星星
  for (let i = 0; i < starCount; i++) {
    stars.push({
      x: random(width),
      y: random(height),
      size: random(1, 3)
    });
  }
}

function draw() {
  // 1. 绘制缓慢变化的背景
  let bgColor = map(sin(frameCount * 0.01), -1, 1, 10, 70);
  background(0, 5, bgColor);
  
  // 2. 绘制所有星星
  for (let star of stars) {
    // 让星星闪烁
    let brightness = random(150, 255);
    stroke(brightness); // 随机亮度
    strokeWeight(star.size);
    point(star.x, star.y);
  }
  
  // 3. 绘制跟随鼠标的彗星
  stroke(255, 255, 200, 150);
  strokeWeight(4);
  line(mouseX, mouseY, pmouseX, pmouseY);
}

// 4. 当鼠标点击时,诞生一颗新星
function mousePressed() {
  stars.push({
    x: mouseX,
    y: mouseY,
    size: random(4, 7) // 新诞生的星星更大
  });
}

image.png

六、课程总结

今天,我们一起走过了从零到一的创意编程旅程。我们:

  1. 搭建了p5.js的本地开发环境。
  2. 理解了p5.js的核心结构 setup() 和 draw()
  3. 学会了如何使用函数绘制基础图形和控制颜色。
  4. 掌握了通过鼠标和系统变量实现动画与交互的核心方法。
  5. 最终,我们亲手创造了一个动态的、可交互的“互动星空”艺术作品。

创意编程展望

今天的课程只是一个开始。p5.js的世界远比我们今天探索的要广阔和深邃。

p5.js官方资源

代码对于艺术家而言,不是一堆冰冷的逻辑,而是我们手中又一种全新的、充满无限可能的媒介。

永远记住:

不要畏惧代码,它和画笔、颜料一样,最终服务于我们的思想、情感和创意的表达。

不要害怕犯错,每一个bug都是一次学习的机会。

七、课后作业:简单彩虹画笔

作业概述:

  • 作业名称:简单彩虹画笔
  • 难度等级: ⭐⭐(简单)
  • 预计完成时间: 30-45分钟
  • 涉及知识点:p5.js图形创建、鼠标交互、颜色变化、键盘控制

作业目标:

创建一个简单的彩虹画笔,移动鼠标可以画出彩虹色的线条,按空格键可以清除画布重新开始。

详细实现步骤:

第一步:创建基础画布

目标: 建立最基础的P5.js项目

步骤说明:

  1. 创建setup()函数,设置画布大小
  2. 创建draw()函数,设置背景色
  3. 使用HSB颜色模式,更容易创造彩虹效果

参考代码:

function setup() {
    // 创建600x400的画布
    createCanvas(600, 400);
    
    // 设置白色背景
    background(255);
    
    // 使用HSB颜色模式,彩虹色更容易实现
    colorMode(HSB);
}

function draw() {
    // 暂时什么都不做
}

验证点:

  • 画布是否正确显示?
  • 背景是否为白色?

第二步:实现鼠标画线

目标: 鼠标移动时画出线条

步骤说明:

  1. 在draw()函数中添加画线代码
  2. 使用line()函数连接鼠标位置
  3. 设置线条的颜色和粗细

参考代码:

function setup() {
    createCanvas(600, 400);
    background(255);
    colorMode(HSB);
}

function draw() {
    // 设置线条颜色为蓝色
    stroke(240, 80, 90);
    
    // 设置线条粗细
    strokeWeight(5);
    
    // 从上一位置到当前位置画线
    line(pmouseX, pmouseY, mouseX, mouseY);
}

验证点:

  • 移动鼠标是否能画出线条?
  • 线条是否为蓝色?
  • 线条粗细是否合适?

第三步:添加彩虹效果

目标: 让线条颜色随时间变化,产生彩虹效果

步骤说明:

  1. 使用frameCount变量获取当前帧数
  2. 用帧数计算颜色值
  3. 颜色在0-360之间循环变化

参考代码:

function setup() {
    createCanvas(600, 400);
    background(255);
    colorMode(HSB);
}

function draw() {
    // 计算彩虹色(0-360循环)
    let rainbowColor = frameCount % 360;
    
    // 设置线条颜色
    stroke(rainbowColor, 80, 90);
    
    // 设置线条粗细
    strokeWeight(5);
    
    // 画线
    line(pmouseX, pmouseY, mouseX, mouseY);
}

验证点:

  • 线条颜色是否随时间变化?
  • 是否能看到彩虹色效果?
  • 颜色变化是否流畅?

第四步:添加清除功能

目标: 按空格键清除画布

步骤说明:

  1. 添加keyPressed()函数
  2. 检测空格键是否被按下
  3. 按下时重新填充背景色

参考代码:

function setup() {
    createCanvas(600, 400);
    background(255);
    colorMode(HSB);
}

function draw() {
    // 计算彩虹色
    let rainbowColor = frameCount % 360;
    
    // 设置线条颜色和粗细
    stroke(rainbowColor, 80, 90);
    strokeWeight(5);
    
    // 画线
    line(pmouseX, pmouseY, mouseX, mouseY);
}

// 添加键盘事件
function keyPressed() {
    // 如果按下空格键
    if (key === ' ') {
        // 清除画布(重新填充白色背景)
        background(255);
    }
}

验证点:

  • 按空格键是否能清除画布?
  • 清除后是否能重新绘画?
  • 彩虹效果是否仍然存在?

第五步:添加操作提示

目标: 在画布上显示操作说明

步骤说明:

  1. 在draw()函数中添加文字显示
  2. 显示简单的操作提示
  3. 设置文字样式

参考代码:

function setup() {
    createCanvas(600, 400);
    background(255);
    colorMode(HSB);
}

function draw() {
    // 计算彩虹色
    let rainbowColor = frameCount % 360;
    
    // 设置线条颜色和粗细
    stroke(rainbowColor, 80, 90);
    strokeWeight(5);
    
    // 画线
    line(pmouseX, pmouseY, mouseX, mouseY);
    
    // 显示操作提示
    fill(0); // 黑色文字
    noStroke(); // 文字无边框
    textAlign(LEFT); // 左对齐
    textSize(16); // 字体大小
    text("移动鼠标绘画,按空格键清除", 10, 30);
}

function keyPressed() {
    if (key === ' ') {
        background(255);
    }
}

验证点:

  • 是否显示操作提示?
  • 文字是否清晰可读?
  • 提示是否影响绘画效果?

第六步:优化绘画效果

目标: 让绘画效果更好看

步骤说明:

  1. 增加线条粗细
  2. 调整颜色饱和度和亮度
  3. 添加简单的淡出效果

参考代码:

function setup() {
    createCanvas(600, 400);
    background(255);
    colorMode(HSB);
}

function draw() {
    // 添加轻微淡出效果,让线条更柔和
    background(255, 255, 255, 10);
    
    // 计算彩虹色
    let rainbowColor = frameCount % 360;
    
    // 设置线条颜色和粗细
    stroke(rainbowColor, 90, 90); // 更鲜艳的颜色
    strokeWeight(8); // 更粗的线条
    
    // 画线
    line(pmouseX, pmouseY, mouseX, mouseY);
    
    // 显示操作提示
    fill(0);
    noStroke();
    textAlign(LEFT);
    textSize(16);
    text("移动鼠标绘画,按空格键清除", 10, 30);
}

function keyPressed() {
    if (key === ' ') {
        background(255);
    }
}

验证点:

  • 线条是否更粗更鲜艳?
  • 是否有轻微的淡出效果?
  • 整体效果是否更好看?

完整参考代码

function setup() {
    // 创建画布
    createCanvas(600, 400);
    
    // 设置白色背景
    background(255);
    
    // 使用HSB颜色模式
    colorMode(HSB);
}

function draw() {
    // 添加轻微淡出效果
    background(255, 255, 255, 10);
    
    // 计算彩虹色(0-360循环)
    let rainbowColor = frameCount % 360;
    
    // 设置线条颜色(高饱和度,高亮度)
    stroke(rainbowColor, 90, 90);
    
    // 设置线条粗细
    strokeWeight(8);
    
    // 画彩虹线
    line(pmouseX, pmouseY, mouseX, mouseY);
    
    // 显示操作提示
    fill(0);
    noStroke();
    textAlign(LEFT);
    textSize(16);
    text("移动鼠标绘画,按空格键清除", 10, 30);
}

// 键盘事件
function keyPressed() {
    // 按空格键清除画布
    if (key === ' ') {
        background(255);
    }
}

提交要求

  1. 完整的P5.js代码文件
  2. 一张你的彩虹画笔作品截图
  3. 简单的一句话感想