p5.js 是一个 JavaScript 库,用于为艺术家、设计师提供更容易上手的创意编程。

它有着完整的一套基于 Canvas 的作画功能,并且你可以把整个浏览器都当成你的“画布”——利用p5.dom.js可以很方便地与其他 HTML 元素进行交互;利用p5.sound.js可以很简单的对声音进行分析与处理。

官网推荐用法

在官网的例子中,推荐使用<script>标签引入的方法:

<html><head><!-- ...... --><script src="https://www.cnblogs.com/xhyccc/archive/2020/12/18/path/to/your/p5.js"></script></head><body><script>function setup() {
createCanvas(300, 500);
}function draw() {}</script></body></html>

这样是让所有的变量、函数都暴露在全局之下,所以我们可以直接使用setup,draw函数进行操作。

但这对于新手来说,这样的做法存在一个致命问题:没有代码提示。并且也不符合我们现代 Web 开发的习惯——模块化。

在动画复杂的情况下,代码会越来越多、越来越邋遢,直到成为一座“屎山”!!!

所以这让我不由得思考起了 p5.js 的最佳实践究竟该如何。

关于最佳实践的思考

p5.js 拥有多个生命周期函数:

用于预加载数据的preload函数;在最开始执行,并且只执行一次的setup函数;一秒执行大概 20 多次的draw函数;鼠标左键点击时触发的mousePressed函数;……

p5 画图的根本其实就是在不同生命周期里对数据进行操作,然后将数据具体转化为图像。

所以按照 MVC 的思想:view = render(data),我们应该拒绝在这些钩子函数中「直接」操作数据——将动画抽象成为独立的 Service(服务),操作数据交给 Service 自己的方法,我们只需要在draw函数中对 Service 内的数据进行绘制即可。

这样讲可能大家还不太明白,那么 talk is cheap, show me the code 吧!

代码提示

为了给 p5 增加代码提示,我们不可以使用官网推荐的方式。需要利用 TypeScript 进行模块化的项目构建(会的同学,或者不需要代码提示的同学可以直接跳过这一节)。

这里需要安装一下 Node.js,直接官网下载即可。

以下步骤仅限 Linux 与 MacOS 平台,Windows 不保证可以这么操作!!!

初始化项目

新建一个文件夹,将文件夹拖入 VSCode 打开,切换至英文输入法,同时按下control + ~就可以开启终端。

在终端输入npm config set registry https://registry.npm.taobao.org && npm init -y && npm i p5 && npm i -D typescript @types/p5 parcel-bundler && ./node_modules/.bin/tsc --init请初学者直接复制粘贴并且回车,不要尝试自己输入。

这时候可以发现我们的文件夹里出现了几个文件,并且他们全都不需要你去关心。

- 你的文件夹名称- node_modules- tsconfig.json- package.json- package-lock.json

打开 package.json 文件,应该长这样:

{"name": "你的文件夹名称",
.....
}

创建启动脚本

我们在第二行插入一些东西(一样请直接复制,不要尝试自己打):

{"scripts": {"start": "./node_modules/.bin/parcel ./src/index.html --no-cache","build": "./node_modules/.bin/parcel build ./src/index.html --no-cache -d build"},"name": "你的文件夹名称",
.....
}

创建 HTML 文件

接着我们建立 src 文件夹,并在其中新建我们的 HTML / CSS 文件:

- 你的文件夹名称- node_modules- tsconfig.json- package.json- package-lock.json- src- index.html- style.css

src 是之后我们唯一需要关注的文件夹。

启动项目

照常的写你的代码,和之前没有任何区别。然后随便在 HTML 中输入一些标签和字符,QQ号买号地图在终端中输入:npm run start并等待一会儿,待显示出

Server running at http://localhost:1234
✨ Built in xxxx.

之后,方可打开浏览器,输入他输出的网址。

此时显示了你刚刚在 HTML 中写的文字即是成功了。

创建 TypeScript 文件

首先在 src 中创建 main.ts 文件,并在 html 中引入。

- src- index.html- style.css- main.ts

因为 p5 的特殊性质,所以无法在 js 文件中提供完整的代码提示,必须使用 typescript。

书写 p5

翻看 p5 的源代码我们发现,p5 本身是一个构造函数,接受两个参数,第一个是一个函数,函数接受一个参数,我们可以通过这个参数实现代码提示:

// main.tsimport P5 from 'p5';const sketch = (p: P5) => {
p.setup = () => {
p.background(0);
};
p.draw = () => {
p.background(0);
p.fill(255);
p.ellipse(0, 0, 25, 25);
};
};

p5 的第二个参数是一个 HTML 元素,意思是我们可以将画布放到这个元素中去。

// index.html<body><div id="app"></div><script src="https://www.cnblogs.com/xhyccc/archive/2020/12/18/main.ts"></script></body>

这时候我们就可以使用new P5(sketch, document.querySelector('#app'));来挂载画布了。

运用 OOP 与 SOA 解耦项目

接下来我们通过一个例子看看如何对项目进行解耦。

要求:了解面向对象的编程。

目标:制作一个漫天繁星。

创建 Star 类

第一步我们需要一个 Star 类。

class 星星 {x坐标 = 0;
y坐标 = 0;
透明度 = 0;
随机放置星星() {// 随机 x y 坐标值}
闪烁() {// 操作透明度}
}

创建 Service

然后再为所有的星星闪烁制作一个服务(服务其实就是一个class)。

class 星星服务 {
星星们 = [];
get 星星的位置() {this.星星们.处理一下((星星) => {
返回 {x: 星星的x坐标,y: y坐标,opacity: 透明度,
}
})
}
创建星星们() {
重复 100 次:this.星星们.增加(new 星星());
}
}

写入 p5

let 星星服务实例;function preload() {
星星服务实例 = new 星星服务class();
}function setup() {
星星服务实例.创建星星们();
}function draw() {
background(0);if (星星服务实例.星星们 的 长度 > 0) {
星星服务实例.星星的位置.遍历((星星) => {
fill(255, 星星.透明度);
ellipse(星星.x, 星星.y, 半径, 半径);
})
}
}

目前动画比较简单,还看不出来该方案有什么优势。

但是一旦动画更加复杂,该方案对原来写法的解耦的优越性将会是毋庸置疑的。

我们仅仅需要的是在不同的生命周期函数中调用服务的方法,然后在draw函数中拿到数据后进行绘图!

模板代码(本节可不看)

需要:Node.js v14.1及以上。

不理解 npm 是什么的可以看这一篇:npm 是什么?。

访问GitHub 仓库下载我事先准备好的模板代码,并进入目录,输入:npm install安装依赖,npm run start启动项目。

PS: 该模板项目使用装饰器完成了 IoC 容器,可能会比较难懂。

(完)


更多相关文章

  1. js变量、常量、函数类型、作用域、闭包、模板字符串
  2. js基础知识(变量、常量、函数、数据类型、函数声明及类型)
  3. 初识JS变量与常量、函数、作用域与闭包、模板字符串与标签函数
  4. js基础语法 变量 函数
  5. Vue组件为什么data必须是一个函数呢?本文案例详解
  6. 解构赋值及其在函数传参中的应用
  7. 华纳负责人yy988680
  8. 1. 变量,常量的区别; 2. 函数的种类与参数类型; 3. 作用域与闭包
  9. 变量,常量的区别+JS数据类型+作用域+闭包

随机推荐

  1. JAVA-全局变量与局部变量-继承-封装-(是三
  2. python 日志简单使用
  3. 关于反序列化时抛出java.io.EOFException
  4. 从RF、BB、AP、外设4个角度看手机的硬件
  5. 忽略转义字符时拆分字符串
  6. java中枚举enum的使用
  7. [刘阳Java]_了解BeanFactory_第4讲
  8. Java多线程六:线程优先级和yield()让步函
  9. JMeter-Java压力测试工具-02
  10. spring cloud微服务分布式云架构-Spring