《点和线的乐章》是
beauty of pixel原创的艺术作品。本教程将会涉及到坐标系和坐标点的介绍,弧度,三角函数,p5.js 颜色的介绍和使用,p5.js 画板的旋转等内容。具体效果参见视频 b站视频
作品分析
仔细观察上面的作品实例图,我们可以发现有如下特点
- 是由直线和点组成
- 点的位置在直线的端点上
- 直线的长度是在有规律地变化
- 直线在不停地旋转,最终形成叠加的运动效果
实现原理
- 以画板中心为圆心
- 让画板以一定的角速度旋转
- 让一个点 A 围绕该圆心做圆周运动,即围绕着圆心运动
- 在该点 A 运动的同时,以该点为圆心,画出另一个围绕该点做圆周运动的点 B
- 连接点 B 前一次的位置和点 B 本次的位置形成一条线段
下面我们一起来实现吧
开始第一步
我们先在画板的中心画一个小圆形,当作圆心
这几行代码主要是 p5.js 内置的函数和变量的使用,其中一些我们在10 PRINT 生成艺术,基于 p5.js 的实现教程里已经见到过
setup和draw都属于 p5.js 的生命周期函数,其中setup只会执行一次,而draw则相当于渲染循环,会一遍又一遍地运行。createCanvas是一个 p5.js 的内置函数,用来创建一个特定尺寸的画板。background是一个设置背景颜色的内置函数,当我们调用了 background 后,画板之前的内容就会被清空掉。windowWidth和windowHeight是我们这次新使用的 p5.js 属性,代表了画板所在页面的宽度和高度。colorMode是一个设置颜色模式的内置函数,我们将本画板的颜色模式设置为HSB,每个颜色分量的值设置为 100。默认情况下,p5.js 的颜色模式是RGB模式,每个颜色的分量值是 255。具体可以参见此篇关于颜色的介绍。
还有一行代码是我们需要重点认识的,那就是 translate(width / 2, height / 2)。
translate 平移坐标系
我们基于 translate(width / 2, height / 2) 实现了平移坐标系,将坐标系的原点平移到了画板的中心,那什么是坐标系、原点和中心点呢?我们来具体了解一下。
笛卡尔坐标系
当我们在进行创意编程时,经常需要处理坐标。例如,我们可能会以某个点为起点画线段,或者在画布的中心画一个圆形。这个点就是表示物体所在的位置,也就是它的坐标。坐标系是表示物体位置的一种方式,其中我们最常使用的是笛卡尔坐标系和极坐标系。这一节我们重点介绍笛卡尔坐标系。
笛卡尔坐标系是一种二维坐标系,由两条直线组成,水平的线称为横轴,也叫 X 轴,垂直的线称为纵轴,也叫 Y 轴,它们交汇的点是原点,其坐标标记为 (0, 0)。
X 轴向左右两个方向水平延伸,而 Y 轴向上下两个方向垂直延伸。
在笛卡尔坐标系中,每个点都有一对坐标 (x, y) 来确定其位置,其中 x 表示点在横轴上的位置,y 表示点在纵轴上的位置。
如下图所示,我们在坐标系上的 (5, 5) 的位置,画了一个圆形。
p5.js 坐标系
需要注意的是,在 p5.js 中,我们使用的坐标系是笛卡尔坐标系的一种变体,坐标系的原点在左上角, x 轴从原点向右延展,y 轴 从原点向下延展。
备注:p5.js 底层是基于浏览器的 canvas 元素来进行绘图的操作,这个翻转的坐标系其实是 canvas 使用的坐标系。关于更多 canvas 的介绍,可以参阅 mdn 的相关文章。
了解完了坐标系和坐标相关的信息,我们再看看这个平移的代码
// 将坐标系原点平移到画板的中心
translate(width / 2, height / 2)
translate(x, y) 是 p5.js 提供的平移画板的函数,它的作用是将画板的原点移动特定的水平偏移量和垂直偏移量。
注意,x, y 是移动的偏移量,而不是移动到点 (x, y) 的位置
width 和 height 是 p5.js 提供的内置变量。width 表示画板的宽度,height 表示画板的高度,那么 width / 2 和 height / 2 就是宽度的一半和高度的一半,正好表示了画板的中心位置。
下图是画板移动到中心点后的示意图。
我们通过代码来认识一下平移的过程。
上述示例代码中,我们先在点 (20, 20) 的位置绘制了一个 20 像素的圆,然后将画板平移到中心位置,再在距离中心 (20, 20) 点的位置绘制了一个 20 像素的圆。再次,我们将画板的原点向下和向右平移了 20 像素,然后再在距离新原点 (20, 20) 的位置又绘制了一个 20 像素的圆。
画出点 A,让它围绕圆心做圆周运动
如何让一个点,绕着另一个点做圆周运动,这是一个数学问题。我们看看如下的示例图。
我们要让点 A 绕着半径为 10 的圆心做圆周运动,那么就需要将点 A 的坐标计算出来。点 A 绕着圆心做圆周运动,变化的其实是点 A 和圆心形成的直线与 X 轴的夹角 θ。
根据任意时刻形成的夹角 θ,我们可以通过勾股定理来计算出点 A 的横坐标 X 和 纵坐标 Y 的值。
勾股定理的计算公式如下:
我们可以得出任意夹角 θ 对应的点 A 坐标代码如下
// 圆半径
const radius = 10
// θ 对应的 X 坐标
const x = radius \* cos(theta)
// θ 对应的 Y 坐标
const y = radius \* sin(theta)
解决了点 A 坐标的问题,接下来就是让点 A 运动起来了,我们只需要让 θ 角不停地从 0 增加即可。代码如下:
上面的代码中,我们定义了一个变量 theta,它代表了当前的夹角值,在每帧的渲染函数 draw 中,我们让 theta 每次增加 0.5,即夹角以 0.5 的变化量依次递增,这样就可以实现圆周运动。
计算并画出点 B
A 点围绕圆心做圆周运动,而 B 点围绕 A 点做圆周运动。同理我们可以计算出点 B 的横坐标 X 和 纵坐标 Y 的值。
需要注意的是,B 点是围绕 A 做圆周运动,所以 B 坐标在计算时,需要加上 A 坐标的 X、Y 值。
连线,点和线的乐章
从上一步的结果看,我们已经离目标很接近了,就差一一条线了,这一步我们来加上这一条线。
我们连线的规则是连接 B 的前一次坐标点和这一次坐标点,形成一条直线。
我们将夹角的改变量做成了可配置,你可以尝试拖动下看看效果。
下面是具体的代码和运行效果。
更进一步
本次教程,我们基于基础的点和线,实现了一个华丽的乐章。不经意间我们修改了某个参数,出来了便是另外一个与众不同的结果,生成艺术和创意编程最大的乐趣或许就在这里,遇见未知的美好。
接下来,你可以做更多的尝试,比如下面一些可能的点:
- 我们实现的连线是 B 的前一次坐标和本次坐标连接,当然也可以简单一点,直接连接 A 点和 B 点形成一条直线,你可以修改一下上面的代码试试。
- 我们目前是绘制了一个点 B,你可以尝试把直线的另一个点也绘制出来看看。
- 尝试改变一下点的大小呢?