当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 网页视觉特效-p5.js完成星际穿越

网页视觉特效-p5.js完成星际穿越

2020年04月18日  | 移动技术网IT编程  | 我要评论

p5.js完成星际穿越特效

欢迎关注我的,⬅️点他即可。

星际穿越,是模仿漫天星辰扑面而来的感觉。

最关键的在于对透视的掌握。

参考资料:the coding train

00 思路构想

  1. 星星是一个圆,会随机的出现在屏幕的任何位置;
  2. 星星会从远处到眼前:圆的大小来表示远近;
  3. 星星的运动轨迹:连接星星与中心点的射线,向外运动。

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)

	// 这里放刚刚新建星星的代码
}

注意,这里不要使用widthheight作为变量名。因为 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 移动星星

移动星星,意味着改变位置,连续的改变位置就会变成移动。因为我们前面说到过,使用一个对象的方法创建的圆,再调用该对象的该方法时,便会重新创建,原来的圆就会消失。

这一步看起来很复杂,但其实非常简单。

我们在更新函数里面,只需要做两件事情:

  1. 减少 z,因为 z 是星星离我们的距离;
  2. 如果星星跑到了我们背后,他们就该重置位置在离我们最远处了。
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)
	}
}

这里我加上了射线,其实非常简单,就是在星星的新位置和起始点画三角形即可。

(完)

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网