思索 p5.js 的最佳实践
本文写于 2020 年 12 月 18 日
p5.js 是一个 JavaScript 库,用于为艺术家、设计师提供更容易上手的创意编程。
它有着完整的一套基于 Canvas 的作画功能,并且你可以把整个浏览器都当成你的“画布”——利用 p5.dom.js
可以很方便地与其他 HTML 元素进行交互;利用 p5.sound.js
可以很简单的对声音进行分析与处理。
官网推荐用法
在官网的例子中,推荐使用 <script>
标签引入的方法:
<html>
<head>
<!-- ...... -->
<script src="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 吧!
例子
目标:制作一个漫天繁星。
第一步我们需要一个 Star 类。
class 星星 {
x坐标 = 0;
y坐标 = 0;
透明度 = 0;
随机放置星星() {
// 随机 x y 坐标值
}
闪烁() {
// 操作透明度
}
}
然后再为所有的星星闪烁制作一个服务(服务其实就是一个 class
)。
class 星星服务 {
星星们 = [];
get 星星的位置() {
this.星星们.处理一下((星星) => {
返回 {
x: 星星的x坐标,
y: y坐标,
opacity: 透明度,
}
})
}
创建星星们() {
重复 100 次:this.星星们.增加(new 星星());
}
}
let 星星服务实例;
function preload() {
星星服务实例 = new 星星服务class();
}
function setup() {
星星服务实例.创建星星们();
}
function draw() {
background(0);
if (星星服务实例.星星们 的 长度 > 0) {
星星服务实例.星星的位置.遍历((星星) => {
fill(255, 星星.透明度);
ellipse(星星.x, 星星.y, 半径, 半径);
})
}
}
目前动画比较简单,还看不出来该方案有什么优势。
但是一旦动画更加复杂,该方案对原来写法的解耦的优越性将会是毋庸置疑的。
我们仅仅需要的是在不同的生命周期函数中调用服务的方法,然后在 draw
函数中拿到数据后进行绘图!
模板代码(本节可不看)
需要:Node.js v14.1
及以上。
上官网下载并安装 Node.js 后,命令行输入 node -v
查看是否安装成功。再输入 npm -v
查看是否成功安装 npm。
不理解 npm 是什么的可以看这一篇:npm 是什么?。
输入 npm config set registry https://registry.npm.taobao.org
将下载源更改为国内地址。
访问GitHub 仓库下载我事先准备好的模板代码,并进入目录,输入:npm install
安装依赖,npm run start
启动项目。
(完)