首页 > 文章列表 > 使用JavaScript生成一个粒子系统

使用JavaScript生成一个粒子系统

JavaScript 生成 粒子系统
249 2023-09-03

如果您曾经见过用 Flash 创建的烟花效果,我将向您展示如何使用纯 JavaScript 重现该效果!为什么要让 Flash 开发者享受所有乐趣?


设置您的工作区

要求

了解 Javascript 对象表示法。如果您可以接受这方面的一些培训,没问题,Leigh Kaszick 有一个有关面向对象 JavaScript 基础知识的深入教程。

Raphael.js 的基础知识。再说一次,如果您是新手,那么您可能需要查看 Damian Dawber 撰写的 Raphael JS 库简介。


什么是粒子系统?

就计算机图形学而言,粒子系统涉及在 3D 模型中复制自然粒子系统的行为,例如爆炸、火灾、云以及大量其他现象,这些现象很难使用传统的编程技术来实现。< /p>

使用JavaScript生成一个粒子系统

基本粒子系统包括 -

  • 发射器 - 空间中粒子起源的点。
  • 舞台 - 放置粒子系统的地方。
  • 力 - 这些是影响粒子运动的外力。在我们的教程中,它将是重力。

步骤 1

我们将从创建粒子图像开始。

打开 Photoshop 并创建一个 25 像素 x 25 像素的画布。

使用JavaScript生成一个粒子系统

创建一个新图层。

使用JavaScript生成一个粒子系统

选择硬度为 100% 的 23px 圆形画笔。在本教程中,我们使用白色,但您可以根据需要选择任何画笔形状或颜色。

使用JavaScript生成一个粒子系统

现在我们必须隐藏背景图层,然后使用“保存为网络和设备”选项,将其另存为 png-24 格式的“article_img”。


步骤 2

现在,让我们开始编码。首先,粘贴以下代码。

 
<html> 
<head> 
<script type="text/JavaScript" src="raphael-min.js"></script> 
<script type="text/JavaScript" src="sps.js"></script> 
<title>Simple Particle System</title> 
</head> 
<body style="background:#000000;color:#CCCCCC" onload="startSystem()"> 
<div id="pane" ></div> 
</body> 
</html>

这是一个简单的 HTML 代码片段,其中包含:

  • 导入 raphael.js 和 sps.js,我们将在其中使用粒子系统的 JavaScript 代码。
  • 然后,在正文中,有一个 ID 为 pane 的 div - 这将是我们的舞台。
  • body 标记还包含一个 startSystem() 函数,该函数将在 onload 事件上调用。我们稍后将在 JavaScript 文件中定义它。

步骤 3

接下来,我们将创建 sps.js,在其中键入粒子系统的实际代码。


步骤 4

由于我们使用的是 2D 模型,为了避免脚本中出现一堆 x,y 变量,我们将创建一个 Vector 类来为我们处理坐标。

 
function Vector() 
{ 
	this.x =0; 
	this.y =0; 
    //this.z =0;    
	 
}

注意:我们不会使用 Z 坐标,因为我们不关心粒子距相机视图(眼睛)的距离,Z 坐标主要处理比例和不透明度属性。


步骤 5

我们需要设置变量值并添加它们的函数,因此我们将在 Vector 类中声明以下函数。

 
function Vector() 
{ 
	this.x =0; 
	this.y =0; 
    //this.z =0;    
	this.set = function(X,Y) // setting the values of X,Y to our object 
	{ 
		with(this) 
		{ 
			x = X; 
			y =Y; 
			//z = Z; 
		} 
	} 
	this.add = function(vector2) // adding another vector's values to our current vector object 
	{ 
		this.x = this.x + vector2.x; 
		this.y = this.y + vector2.y; 
		//this.z = this.z + vector2.z;	 
	} 
}

注意:如果使用的话,请务必在设置的函数参数中添加 Z。


第 6 步

让我们创建一个 Vector 对象来保存粒子起源的坐标;另外,我们将声明一个全局 Raphael 对象和一个稍后将使用的计时器变量。

 
var canvas;  // our raphael object 
var timer;  
var emitter = new Vector(); 
emitter.set(400,200);

步骤 7

我们需要为粒子对象创建一个类,它将包含基本属性,例如:

  • 颜色和形状 - 粒子的基本属性。
  • 大小 - 粒子的大小。
  • 位置 - 起点。
  • 速度 - 速度(此处指粒子在舞台上的扩散)。
  • 加速 - 速度增加。
  • 寿命 - 粒子的寿命。

在 JavaScript 世界中,我们将稍微更改一些变量,例如在 Photoshop 中创建图像时已经定义的颜色和大小。

 
function Particle() 
{ 
this.size = Math.random() * 10 + 15;  
this.particle =  canvas.image("particle_img.png",emitter.x,emitter.y,this.size,this.size); 
this.loc = new Vector(); 
this.vel = new Vector(); 
this.acc = new Vector(); 
this.lifespan = Math.random() * 250; 
}

在 JavaScript 中,类是使用 function 声明的,其变量是使用 this 声明的。

  • 我们希望粒子的大小是随机生成的,并且在 15 到 25 之间。
  • Particle 是 Raphael 图像对象,以图像名称、坐标和大小作为参数。
  • loc 是粒子的位置。
  • acc 是加速度。
  • vel 是速度。
  • 寿命是粒子在舞台上生存的时间。

步骤 8

我们需要为以下条件创建函数:

  • 使用上述声明的属性在画布上创建一个粒子。
  • 不断更新其加速度的位置。
  • 检查粒子是否已死,然后我们必须将其从画布中删除,否则我们的舞台将充满死粒子:)。

步骤 9

首先,我们将声明 init 函数,用于初始化粒子。

 
 this.init = function(){ 
	with(this) 
	{ 
		particle.rotate(Math.random()*360);  
		acc.set(0,0.05); 
		vel.set(Math.random() * 4 -2,Math.random() * 3 -1); 
		loc.set(emitter.x,emitter.y);	 
	}		 
}

这里我们将代码包装在 with(this) 中,它引用当前的粒子对象。

  • article.rotate() 用于在 0 到 360 度之间随机旋转对象。在这种情况下,这并不重要,因为我们使用的是圆形物体,但是,对于其他物体,例如星星,它确实很重要,因为所有粒子都具有相同的几何角度,这看起来很奇怪。
  • 我们将初始加速度设置为0,0.5,因为我们想要说明粒子如何与重力结合;这就是我们初始化 y 坐标的原因。当我们增加加速度值时,粒子的速度会增加得更快。
  • 这里,vel 指的是粒子在舞台上的散布 - 即 Math.random() * 4 -2 将生成 -2 到 2 范围内的数字。
  • loc 获取粒子的初始值。

需要注意的几点:

  1. 加速条件
    • 正值会增加加速度。
    • 对于无重力阶段,加速度 = 0。
    • 对于反重力,应使用负值。
  2. vel的条件
    • 为了增加分布,我们将使用 Math.random() 生成的范围的更大值。
    • 对于在特定一侧发射粒子,我们可以通过乘以最大正数或负数来实现。

使用JavaScript生成一个粒子系统


第 10 步

我们现在将创建一个函数来将粒子的值更新到新位置。

 
this.update = function(){ 
	with(this) 
	{ 
		vel.add(acc); 
		loc.add(vel); 
		lifespan -= 1; 
		particle.animate({x:loc.x,y:loc.y},39); 
	}	 
}

这里,每当调用更新函数时,加速度就会添加到 vel 中,然后添加到位置中。最后,粒子被更新到新位置。当添加恒定加速度时,我们会给人一种粒子受到某种力的印象,因为它必须在单位时间内覆盖更远的距离。另外,每当调用 update 时,它​​的寿命就会减少 1。


步骤 11

最后,对于粒子类,我们将创建一个函数来检查粒子的生命周期是否已结束。

 
this.dead = function() 
	{ 
		if(this.lifespan<0) 
		{ 
			return true; 
		} 
		else  
		{ 
			return false; 
		} 
	}

第 12 步

我们的 Particle 类看起来像这样:

 
function Particle() 
{ 
	 
	this.size = Math.random() * 10 + 15;  
	this.particle =  r.image("particle_img.png",emitter.x,emitter.y,this.size,this.size); 
	this.loc = new Vector(); 
    this.vel = new Vector(); 
    this.acc = new Vector(); 
    this.lifespan = Math.random() * 250; 
    this.init = function(){ 
		with(this) 
		{ 
			particle.rotate(Math.random()*360); 
			acc.set(0,0.05); 
			vel.set(Math.random() * 4 -2,Math.random() * 3 -1); 
			loc.set(emitter.x,emitter.y); 
			 
		} 
		 
		} 
	this.update = function(){ 
		with(this) 
		{ 
			vel.add(acc); 
			loc.add(vel); 
			lifespan -= 1; 
			particle.animate({x:loc.x,y:loc.y},39); 
		} 
		 
		} 
		 
	this.dead = function() 
	{ 
		if(this.lifespan<0) 
		{ 
			return true; 
		} 
		else  
		{ 
			return false; 
		} 
	} 
	 
}

第 13 步

现在我们将创建一个粒子系统类。让我们从声明成员变量开始。

 
this.particles = new Array();

在这里,粒子是我们即将创建的粒子对象的数组。


第 14 步

现在我们需要创建用于初始化和运行粒子系统的函数;所以我们首先创建我们的 init 函数。

 
this.init = function(num) 
	{ 
		 for (var i = 0; i < num; i++)  
		 { 
          this.particles[i] = new Particle(); 
		  this.particles[i].init(); 
		 } 
		 
	}

首先,我们将要创建的粒子数量作为函数参数。然后,使用 for 循环创建粒子对象并通过调用其 init 函数来初始化它们。


第 15 步

现在,我们将创建一个运行函数来运行我们的粒子系统。

 
this.run = function(){ 
		with(this){ 
					timer = setInterval(function(){ 
					for (i=particles.length -1; i>=0; i--)  
					{ 
 		      			if(particles[i].dead()) { 
        					particles[particles.length -1].particle.remove(); 
							particles.pop(); 
							var temp = new Particle(); 
				 			temp.init(); 
				 			particles.unshift(temp);  
							} 
						particles[i].update(); 
			   		} 
				 },40); 
	 
			} 
	   
		}

这里我们的代码被包装在 with(this) 块中,它引用当前的粒子系统对象。现在,由于我们的粒子需要不断更新到下一个位置,因此我们在 setInterval() 函数内调用粒子的 update() 函数,该函数在定义的时间间隔内执行该函数。我们通过迭代粒子数组来更新所有粒子的定位。

现在我们还想检查粒子是否死亡。我们通过调用粒子的 dead() 函数来完成此任务。如果一个粒子死了,那么我们会执行以下操作:

  • 将其从我们的数组中删除。
  • 从死粒子索引之后的位置开始将元素向后移动一位。
  • 将一个新粒子推到数组末尾。
 
var p = particles[i]; 
	for(var j=i;j<particles.length-1;j++) 
	{ 
 		particles[j] = particles[j+1]; 
	} 
p.particle.remove(); 
var temp = new Particle(); 
temp.init(); 
particles.push(temp);

上面的代码是最佳解决方案,但是当我们在 JavasSript 世界中实现它时,排放是如此自发,以至于引擎挂起。这就是为什么我们使用替代代码来增加复杂性并渲染更流畅的动画。你可以自己尝试一下;)。

由于时间周期以毫秒为单位,这就是为什么尽管使用更复杂的算法,我们还是会得到自发发射。


第 16 步

此时,我们的 ParticleSystem 类看起来像这样。

 
function ParticleSystem() 
{ 
	this.particles = new Array(); 
	this.init = function(num) 
	{ 
		 
		 
		 for (var i = 0; i < num; i++)  
		 { 
          this.particles[i] = new Particle(); 
		  this.particles[i].init(); 
		 } 
		  
		 
	} 
	 
 
	this.run = function(){ 
		with(this){ 
	timer = setInterval(function(){ 
				for (i=particles.length -1; i>=0; i--)  
			{ 
 		      if(particles[i].dead()) { 
        		 
				particles[particles.length -1].particle.remove(); 
				 particles.pop(); 
				var temp = new Particle(); 
				 temp.init(); 
				 particles.unshift(temp);  
							 
			} 
			 
			particles[i].update(); 
			 
    		} 
				 },40); 
	 
		} 
	   
		}		 
}

步骤 17

现在我们将声明 startSystem() 函数来启动粒子系统。

 
startSystem = function(){ 
W = 800; H = 400; 
canvas = Raphael("pane", W, H); 
var ps = new ParticleSystem(); 
var iniVec = new Vector(); 
iniVec.set(emitter.x,emitter.y); 
ps.init(20,iniVec); 
 ps.run(); 
}

我们用舞台 ID、宽度和高度初始化 raphael 对象。然后,我们创建粒子系统对象并创建一个具有原点坐标的矢量对象。然后我们通过调用 init 来初始化粒子系统,init 又初始化粒子。我们还在参数中提供了 20 作为粒子数。

粒子数量必须在 20 到 30 之间才能获得流畅的动画效果。提供更高的数字将使浏览器崩溃或挂起!


第 18 步

此时,我们的 sps.js 文件的代码如下所示。

 
 
function Vector() 
{ 
	this.x =0; 
	this.y =0; 
    //this.z =0;    
	this.set = function(X,Y) 
	{ 
		with(this) 
		{ 
			x = X; 
			y =Y; 
			//z = Z; 
		 
		} 
	} 
	this.add = function(vector2) 
	{ 
		 
		 
		 
		this.x = this.x + vector2.x; 
		this.y = this.y + vector2.y; 
		//this.z = this.z + vector2.z; 
		 
		 
	} 
} 
 
 
var canvas;  // our raphael object 
var timer;  
var emitter = new Vector(); 
emitter.set(400,200); 
 
function Particle() 
{ 
	 
	this.size = Math.random() * 10 + 15;  
	this.particle =  canvas.image("img1.png",emitter.x,emitter.y,this.size,this.size); 
	this.loc = new Vector(); 
    this.vel = new Vector(); 
    this.acc = new Vector(); 
    this.lifespan = Math.random() * 250; 
    this.init = function(){ 
		with(this) 
		{ 
			particle.rotate(Math.random()*360); 
			acc.set(0,0.05); 
			vel.set(Math.random() * 4 -2,Math.random() * 3 -1); 
			loc.set(emitter.x,emitter.y); 
			 
		} 
		 
		} 
	this.update = function(){ 
		with(this) 
		{ 
			vel.add(acc); 
			loc.add(vel); 
			lifespan -= 1; 
			particle.animate({x:loc.x,y:loc.y},39); 
		} 
		 
		} 
		 
	this.dead = function() 
	{ 
		if(this.lifespan<0) 
		{ 
			return true; 
		} 
		else  
		{ 
			return false; 
		} 
	} 
	 
} 
 
 
function ParticleSystem() 
{ 
	this.particles = new Array(); 
	this.init = function(num) 
	{ 
		 
		 
		 for (var i = 0; i<num; i++)  
		 { 
          this.particles[i] = new Particle(); 
		  this.particles[i].init(); 
		 } 
		  
		 
	} 
	 
 
	this.run = function(){ 
		with(this){ 
	timer = setInterval(function(){ 
				for (i=particles.length -1; i>=0; i--)  
			{ 
 		      if(particles[i].dead()) { 
        		 
				particles[particles.length -1].particle.remove(); 
				 particles.pop(); 
				var temp = new Particle(); 
				 temp.init(); 
				 particles.unshift(temp);  
							 
			} 
			 
			particles[i].update(); 
			 
    		} 
				 },40); 
	 
		} 
	   
		} 
				 
} 
 
startSystem = function(){ 
W = 800; H = 400; 
  canvas = Raphael("pane", W, H); 
  var ps = new ParticleSystem(); 
	ps.init(20); 
	 ps.run(); 
       
}

终于,我们完成了。这个小项目兼容所有浏览器,甚至在 Internet Explorer 中,尽管 IE6 确实存在一些透明度问题。这个粒子系统几乎可以在任何你喜欢的地方使用,包括背景、爆炸效果、标志和标题介绍等。我制作了三个演示,它们是上面代码的修改版本。其中之一实现了不同的粒子系统,例如烟雾、灰尘和散景粒子系统。享受,感谢您的阅读!

来源:https://code.tutsplus.com/generating-a-particle-system-with-javascript--net-10668t
上一篇 JDBC 语句接口的 setFetchSize() 和 setMaxRows() 方法有什么用? 下一篇 PHP - 使用mb_strtolower()函数将字符串转换为小写字母

本类最新

查看更多