网页视觉特效-p5.js完成星际穿越
p5.js完成星际穿越特效
欢迎关注我的,⬅️点他即可。
星际穿越,是模仿漫天星辰扑面而来的感觉。
最关键的在于对透视的掌握。
参考资料:the coding train
00 思路构想
- 星星是一个圆,会随机的出现在屏幕的任何位置;
- 星星会从远处到眼前:圆的大小来表示远近;
- 星星的运动轨迹:连接星星与中心点的射线,向外运动。
01 创建星星
我们可以使用一个 star 类,来用它表示我们的星星。
class star { }
星星的成员变量有哪些?星星的位置、大小和运动的速度:
class star { constructor() { this.x = random(-width / 2, width / 2) // 随机x坐标 this.y = random(-width / 2, width / 2) // 随机y坐标 this.z = random(0, width) // 随机z坐标 this.r = 25 + random(-2, 3) // 随机半径 } // 在当前位置画圆 show() { fill(255) nostroke() ellipse(this.x, this.y, this.r, this.r) } }
这里为什么是-width/2 到 width/2 呢?
而且为什么 y 的值也要取决于 width 呢?
因为 p5 的原点是在左上角,我们的星星从原点计算起会很方便,一会儿我们会通过一条语句,将整个画面往右下挪动,使原点在画面中呈现!
y 的值之所以取决于 width,是因为电脑一般都是长方形,并且 width>height。
如果是分别根据 width 和 height 随机值,就会导致分布不均匀,变成蝴蝶型(左右两边集中,上下很稀疏)。
我们还需要什么?
因为星星会不断的移动,所以说我们需要绘制它移动的轨迹。但是首先,我们需要初始化星星:
let stars = [] // 存放星星的数组 function setup() { const starsnumber = 100 // 星星的个数 for (let i = 0; i < starsnumber; i++) { const temp = new star() stars.push(temp) } }
现在我们初始化了 100 个星星,但是画布忘记绘制了。我们不妨利用 css 和 js,让用户浏览器无论是多大,我们都刚刚好全屏显示。
让我们来写一点点 css:
* { margin: 0; padding: 0; } body { width: 100vw; height: 100vh; }
然后需要用到:document.body.offsetwidth
获取宽度,同理,offsetheight
可以获取高度:
function setup() { const wid = document.body.offsetwidth const heig = document.body.offsetheight createcanvas(wid, heig) // 这里放刚刚新建星星的代码 }
注意,这里不要使用width
与height
作为变量名。因为 p5 中,width 与 height 就代表当前画布宽高。
所以归纳一下,第一阶段,在 setup 方法和 draw 方法中,应该这么写:
let stars = [] function setup() { const wid = document.body.offsetwidth const heig = document.body.offsetheight createcanvas(wid, heig) const starsnumber = 100 for (let i = 0; i < starsnumber; i++) { const temp = new star() stars.push(temp) } } function draw() { background('#000') for (let i = 0; i < stars.length; i++) { stars[i].show() } }
这样就在屏幕上绘制出了 100 个静态的星星。
02 移动星星
移动星星,意味着改变位置,连续的改变位置就会变成移动。因为我们前面说到过,使用一个对象的方法创建的圆,再调用该对象的该方法时,便会重新创建,原来的圆就会消失。
这一步看起来很复杂,但其实非常简单。
我们在更新函数里面,只需要做两件事情:
- 减少 z,因为 z 是星星离我们的距离;
- 如果星星跑到了我们背后,他们就该重置位置在离我们最远处了。
update(speed) { z = z - seed; if(z <= 1) { z = width; x = random(0, width); y = random(0, height); } }
这里可能有人会疑惑:为什么第一次初始化星星的时候,是random(0, width)
,现在让 z 的值直接等于 width 呢?
因为我们第一打开网页的时候,星星出现,这个时候星星应该是有远的有近的。
可是当我们星星飞走了,重新生成的时候,他就该从无限远处进入视野,而不是直接出现在眼前。
接下来我们不需要关心 x 和 y 的值应该怎样变化,因为可以通过某种计算,将 z 的变化,线性的表现在 x,y 上。
这里需要提到一点,我们在这里利用了透视的原理,实际上,x 与 y 的值是不会改变的,只是我们的透视视角,让他们看起来,是一条斜线。
更新完了位置,我们需要在 show 方法中,通过计算表示出新的 x、y 了。
show() { fill(255); // 上色 nostroke() const nowx = map(this.x / this.z, -1, 1, -width / 2, width / 2) const nowy = map(this.y / this.z, -1, 1, -width / 2, width / 2) const nowr = map(this.z, 0, width, this.r, 0) ellipse(nowx, nowy, nowr, nowr) }
紧接着我们只需要在主方法里,不断的循环update()
和show()
即可!
03 大功告成
function draw() { background(0); translate(width / 2, height / 2); for(int i = 0; i < stars.length; i++) { stars[i].update(speed); stars[i].show(); }
04 附录:完整代码
let stars = [] function setup() { const wid = document.body.offsetwidth const heig = document.body.offsetheight createcanvas(wid, heig) let starsnumber = parseint((width * height) / 6500) for (let i = 0; i < starsnumber; i++) { const temp = new star() stars.push(temp) } } function draw() { translate(wid / 2, heig / 2) background('#000') for (let i = 0; i < stars.length; i++) { stars[i].update(15) stars[i].show() } }
class star { constructor() { this.x = random(-width / 2, width / 2) this.y = random(-width / 2, width / 2) this.z = random(0, width) this.r = 25 + random(-2, 3) this.ismiss = false this.sx this.sy } update(speed) { this.z -= speed if (this.z <= 1) { this.x = random(-width / 2, width / 2) this.y = random(-width / 2, width / 2) this.z = width this.ismiss = false } } show() { fill(255) nostroke() const nowx = map(this.x / this.z, -1, 1, -width / 2, width / 2) const nowy = map(this.y / this.z, -1, 1, -width / 2, width / 2) if (!this.ismiss) { this.sx = nowx this.sy = nowy this.ismiss = true } const nowr = map(this.z, 0, width, this.r, 0) ellipse(nowx, nowy, nowr, nowr) stroke(255) triangle(nowx + nowr / 3, nowy + nowr / 3, nowx - nowr / 3, nowy - nowr / 3, this.sx, this.sy) } }
这里我加上了射线,其实非常简单,就是在星星的新位置和起始点画三角形即可。
(完)
推荐阅读