粒子文字效果

Ispooky的博客中看到了一个文字粒子特效,效果如下:

今天有网友问起,恰巧之前研究过,分享出来给大家。

首先要清楚,本例中粒子的运动是一种叫做Spring的运动方式,你可以在这里找到详细的解释。下面来看看效果:
当鼠标移动至白色圆圈内时,小球向鼠标做Spring运动,当鼠标移出白色圆圈时,小球向舞台中心做Spring运动

具体的实现过程,我在下面的代码中已经进行了详细解释,这里就不再赘述了,上代码:

Particle类

package
{
	/**
	 * ...
	 * @author ladeng6666
	 */
	public class Particle
	{
		public var x:Number, y:Number;
		public var color:Number = 0;
		//当前粒子的初始坐标、速度、摩擦力
		private var initx:Number, inity:Number;
		private var vx:Number=0, vy:Number=0;
		private var friction:Number = 1;

		private var targetX:Number, targetY:Number;//目标位置坐标
		private var _step:Number = 10;//移动步数
		private var _disToInit:Number;//到初始位置的距离

		public function Particle(px:Number = 0,py:Number=0 )
		{
			initx = x = px;
			inity = y = py;

			targetX = initx;
			targetY = inity;
		}

		//回到初始位置,添加摩擦力
		public function backToInitPos():void {
			targetX = initx;
			targetY = inity;
			friction = 0.96;

			move();
		}
		//移动至目标位置,移除摩擦力
		public function moveTo(tx:Number, ty:Number):void {
			targetX = tx;
			targetY = ty;
			friction = 1;

			move();
		}
		//更新粒子坐标
		private function move():void {
			_disToInit = distanceTo(targetX, targetY);
			_step = _disToInit;

			//如果粒子与目标位置距离小于1,将粒子置于目标位置
			if (_disToInit<1){
				x = initx;
				y = inity;
				return;
			}
			if (_step == 0) return;
			//spring算法
			vx += (targetX - x) / _step;
			vy += (targetY - y) / _step;
			vx *= friction;
			vy *= friction;
			x += vx;
			y += vy;

		}
		//计算该点与点(x1,y1)之间的距离
		public function distanceTo(x1:Number, y1:Number):Number {
			var dx = x - x1;
			var dy = y - y1;
			return Math.sqrt(dx * dx + dy * dy);
		}
		public function toString():String {
			return "x:" + x + ",y:" + y + ";";
		}
	}

}

ParticleTest类

package
{
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;

	/**
	 * ...
	 * @author ladeng6666
	 */
	public class ParticleTest extends Sprite
	{
		private var disSpec:Number = 140;
		private var disToInit:Number, disToTarget:Number;

		private var particleView:Sprite;
		private var particle:Particle;

		public function ParticleTest()
		{
			addEventListener(Event.ADDED_TO_STAGE, init);
		}

		private function init(e:Event):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);

			particleView = createBall();
			particleView.x = stage.stageWidth / 2;
			particleView.y = stage.stageHeight / 2;
			particle = new Particle(particleView.x, particleView.y);

			addChild(particleView);
			addBallAtCenter();
			stage.addEventListener(Event.ENTER_FRAME, loop);
		}

		private function loop(e:Event):void
		{
			//计算粒子与鼠标的距离
			var disToTarget:Number = particle.distanceTo(mouseX, mouseY);
			if (disToTarget < disSpec) {
				//若在规格距离内,在向鼠标做spring运动
				particle.moveTo(mouseX, mouseY);
			}else {
				//否则向粒子初始位置-即舞台中心-做spring运动
				particle.backToInitPos();
			}
			particleView.x = particle.x;
			particleView.y = particle.y;
		}
		//创建一个圆
		private function createBall():Sprite {
			var shape:Sprite = new Sprite();
			shape.graphics.beginFill(0xff0000,0.5);
			shape.graphics.drawCircle(0, 0, 10);
			shape.graphics.endFill();
			shape.graphics.lineStyle(1, 0xFFFFFF,0.3);
			shape.graphics.drawCircle(0, 0, disSpec);
			return shape;
		}
		//在舞台中间绘制一个绿色的小球,表示中心点
		private function addBallAtCenter():void {
			graphics.beginFill(0x00FF00,1);
			graphics.drawCircle(stage.stageWidth / 2, stage.stageHeight / 2, 5);
			graphics.endFill();
		}

	}

}

接下来,就是对图像抽样,将每个像素看成一个粒子,然后做上面的Spring运动,具体步骤:

  1. 用一个名为srcBmd的BitmapData把源图像的数据draw下来
  2. 遍历srcBmd对象,将所有的像素存储为粒子
  3. 在EnterFrame侦听函数中,遍历所有粒子,根据粒子与鼠标的距离,分别在鼠标和初始位置间做Spring运动
  4. 用另外一个effectBmd加一些效果,显示在舞台上

我把Ispooky的文字换成了照片,道理是一样的,看效果:

代码如下,同样我做了详细的注释:

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.BlurFilter;
	import flash.display.BlendMode;
	import flash.geom.Point;

	/**
	 * ...
	 * @author ladeng6666
	 */
	public class MakeParticlesFinal extends Sprite
	{
		private var srcBmd:BitmapData;//源图像数据
		private var effectBmd:BitmapData;//目标图像数据
		private var effectBmp:Bitmap;//目标图像

		private var img:MovieClip;//图像来源,拉登的靓照,哈哈
		private var emptyMask:MovieClip;//黑色半透明矩形,用来消除粒子

		private var step:int = 2;//粒子抽样间隔数
		private var particlesList:Array;//用来存储粒子的数组
		private var _disToTarget:Number;//粒子到目标位置的距离

		public function MakeParticlesFinal()
		{
			particlesList = new Array();

			//实例化源图像,不用添加到舞台上,因为我实际要看到的是effectBmp
			img = new Ladeng();
			img.x = (stage.stageWidth - img.width) / 2;
			img.y = (stage.stageHeight - img.height) / 2;
			emptyMask = new EmptyMask();

			//实例化源图像数据和目标图像数据,尺寸与舞台一致,否则图像尺寸外的粒子无法渲染
			srcBmd = new BitmapData(550,400, false, 0x00000000);
			effectBmd = new BitmapData(550, 400,true, 0);
			effectBmp = new Bitmap(effectBmd);
			addChild(effectBmp);

			//提出源图像数据
			srcBmd.draw(img);
			//根据源图像数据,创建粒子
			makeParticles();

			addEventListener(Event.ENTER_FRAME, loop);
		}
		private function makeParticles():void {
			var color:Number = 0;
			//遍历图像的所有像素
			for (var i:int=0; i <= img.width; i += step) {
				for (var j:int=0; j <= img.height; j += step) {
					color = srcBmd.getPixel32(i, j);
					//只提取颜色不为空的像素
					if(color!=0x0){
						var p:Particle = new Particle(i+img.x, j+img.y);
						p.color = color;
						particlesList.push(p);
					}
				}
			}
		}
		private function loop(e:Event):void {
			effectBmd.lock();//lock一下,节省CPU计算
			effectBmd.draw(emptyMask);//draw黑色半透明矩形框,清除effectBmd数据
			srcBmd.lock();
			srcBmd.draw(emptyMask);//draw黑色半透明矩形框,清除srcBmd数据

			//遍历所有的粒子
			for each(var p:Particle in particlesList) {
				//计算粒子到鼠标的距离
				_disToTarget = p.distanceTo(mouseX, mouseY);
				//距离乘以一个随机值,让粒子的运动更加随机真实一点,如果小于100,就向鼠标做spring运动
				if (_disToTarget*(1+Math.random()) < 100) {
					p.moveTo(mouseX, mouseY);
				}else {
					//否则想初始坐标做spring运动
					p.backToInitPos();
				}
				//将粒子填充到effectBmd中
				effectBmd.setPixel32(p.x, p.y, p.color);
			}
			//srcBmd再绘制一遍effectBmd
			srcBmd.draw(effectBmd);
			//然后再添加模糊滤镜
			srcBmd.applyFilter(srcBmd, srcBmd.rect, new Point(), new BlurFilter(30, 30));
			//最后叠加到effectBmd中,实现模糊效果
			effectBmd.draw(srcBmd, null, null, BlendMode.ADD);

			srcBmd.unlock();
			effectBmd.unlock();
		}

	}

}

点击下载源文件

下一步,Ispooky的效果中,在Particle中绘制了细小的线段,所有粒子有一段轨迹,下次我们就来研究这个效果。

联系作者

公众号:拉小登 | 微博:拉登Dony | B站:拉小登Excel

20 Replies to “粒子文字效果”

  1. 感谢提供思路。鉴于对嵌套for循环的恐惧,取像素的时候改为了
    private function createParticles( ):void
    {
    _colorVec = _particleBMD.getVector(_particleBMD.rect);

    var temLen:int = _colorVec.length;
    for (var index:String in _colorVec){
    var color:uint = _colorVec[index];
    var temAlpah:uint = color>>24;
    if(temAlpah != 0 ){
    var temVO:SpringParticle = new SpringParticle( );
    var temX:Number = int(index)%_particleBMD.rect.width;
    var temY:Number = int(index)/_particleBMD.rect.width;
    temVO.x = temVO.targetX = temVO.initX = temX;//temX == 0?_particleBMD.rect.width:temX;
    temVO.y = temVO.targetY = temVO.initY = temY;//temY == Math.floor(temY)?(temY-1):Math.floor(temY);
    temVO.sourceColor = temVO.color = color;
    temVO.index = int(index);
    temVO.fraction = 0.98;
    _particles.push(temVO);
    }
    }
    }
    设置像素的时候用到了setVector

  2. 很受启发哈!用这个原理可以把切水果游戏里的各种水果图片切成微粒拼接在一起,就可以实现任意角度任意位置切开水果了,还可以反复切成任意小块!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注