粒子文字效果
文章大纲
在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运动,具体步骤:
- 用一个名为srcBmd的BitmapData把源图像的数据draw下来
- 遍历srcBmd对象,将所有的像素存储为粒子
- 在EnterFrame侦听函数中,遍历所有粒子,根据粒子与鼠标的距离,分别在鼠标和初始位置间做Spring运动
- 用另外一个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中绘制了细小的线段,所有粒子有一段轨迹,下次我们就来研究这个效果。
联系作者
碉堡了,帅呆了,太震撼了这效果,谢谢师傅
真厉害
谢谢你的评论!
谢谢,重点是你能看懂,L0L!
i think the information on this subject is still very scarce at internet.http://www.nomedodominio.com
拉登叔你贴上来的代码Particle类第51行 if (_disToInit<1){
< 变成了 <
源码包里是没错的~
特殊符号编码问题,搞不定,还是下载源码看吧,sorry啦!
感谢提供思路。鉴于对嵌套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
用vector存取数据会相对快些吧,另外getVector是返回的指定区域的像素颜色值吧!
谢谢你的回复,受教啦!
很受启发哈!用这个原理可以把切水果游戏里的各种水果图片切成微粒拼接在一起,就可以实现任意角度任意位置切开水果了,还可以反复切成任意小块!
我自学AS3一直关注你的东西 很实用 感谢你 虽然我用的都是简单的功能
楼主源码来一份吧
右击代码下面的 源码下载 ,然后另存为即可!!
貌似源码都下载不了了@ladeng6666
貌似,a标签的是http://vdisk.weibo.com/s/4GXm1这个东西,提示被删除了,as新手啊!求分享源码
yfqkeycoding@gmail.com
拉登大师,源码无法下载,请给一份!
已更新附件,谢谢你的关注!
不好意思,已经更新源码地址了,给你带来的不变,请见谅!
请重新下载!
照着博主的思路写了一个C#版本的实现,不过性能差好多….
效果太绚丽了,超喜欢!