40000个BitmapData粒子的喷射动画很好地体现了BitmapData粒子在性能方面的优势,但视觉效果并不十分理想,能否使用BitmapData粒子实现更炫更复杂的视觉效果呢?下面就让我们模拟图片粒子化的动画效果。完成的效果是首先加载一张位图,单击鼠标后该位图粒子化并上升飞起,再次单击鼠标则向上飞起的粒子又重新生成初始的图片。类文件名为ParticleImage.as,源代码如下:
package{ import com.particles.Particle2D; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.ColorTransform; import flash.geom.Point; [SWF(width="800", height="600", frameRate="31")] public class ParticleImage extends Sprite{ [Embed(source="assets/portrait.jpg")] private var image:Class; private var pixels:Array=new Array(); private var particles:Array=new Array(); private var portrait:Bitmap; private var colorTransform:ColorTransform=new ColorTransform(0.8, 0.8, 0.8, 0.8); private var status:Array=new Array(); private var sw:int=800; private var sh:int=600; private var bmpd:BitmapData; private var bmp:Bitmap; private var centerY:Number; private var centerX:Number; private var rows:int=0; public function ParticleImage(){ stage.addEventListener(MouseEvent.CLICK, onBreakClick); initBitmap(); } private function initBitmap():void{ var x:int=0; var y:int=0; portrait=new image(); bmpd=new BitmapData(sw, sh, false, 0); centerX=(bmpd.width - portrait.width) / 2; centerY=(bmpd.height - portrait.height) / 2; bmpd.copyPixels(portrait.bitmapData, portrait.bitmapData.rect, new Point(centerX, centerY), null, new Point(), true); bmp=new Bitmap(bmpd); addChild(bmp); y=0; while (y < portrait.height){ pixels[y]=[]; particles[y]=[]; x=0; while (x < portrait.width){ pixels[y].push(portrait.bitmapData.getPixel32(x, y)); particles[y].push(new Particle2D(centerX + x, centerY + y, 0, 0, 1 + Math.random() * 5)); x++; } y++; } } private function onBreakClick(event:MouseEvent):void{ var xmouse:Number=mouseX; var ymouse:Number=mouseY; event.currentTarget.removeEventListener(MouseEvent.CLICK, onBreakClick); event.currentTarget.addEventListener(MouseEvent.CLICK, onRecoverClick); var y:int=0; while (y < portrait.height){ var x:int=0; while (x < portrait.width){ var p:Particle2D=particles[y][x] as Particle2D; var dx:Number=p.x - xmouse; var dy:Number=p.y - ymouse; var square:Number=Math.sqrt(dx * dx + dy * dy); var radian:Number=Math.atan2(dy, dx); var radius:Number=(10 + Math.random() * 90) * 20 / (20 + square); p.vx=p.vx + radius * Math.cos(radian) / p.mass; p.vy=p.vy + radius * Math.sin(radian) / p.mass; x++; } y++; } removeEventListener(Event.ENTER_FRAME, onRecoverEnterFrame); addEventListener(Event.ENTER_FRAME, onBreakEnterFrame); } private function onBreakEnterFrame(e:Event):void{ bmpd.lock(); bmpd.colorTransform(bmpd.rect, colorTransform); var y:int=0; while (y < portrait.height){ var x:int=0; while (x < portrait.width){ var p:Particle2D=particles[y][x] as Particle2D; if (p.y > -1){ p.vx=p.vx * 0.98; p.vy=p.vy - 1 / p.mass; p.update(); } bmpd.setPixel32(p.x, p.y, pixels[y][x]); x++; } y++; } bmpd.unlock(); } private function onRecoverClick(event:MouseEvent):void{ event.currentTarget.removeEventListener(MouseEvent.CLICK, onRecoverClick); event.currentTarget.addEventListener(MouseEvent.CLICK, onBreakClick); rows=0; var y:int=0; while (y < portrait.height){ var x:int=0; status[y]=[]; while (x < portrait.width){ status[y][x]=false; x++; } y++; } removeEventListener(Event.ENTER_FRAME, onBreakEnterFrame); addEventListener(Event.ENTER_FRAME, onRecoverEnterFrame); } private function onRecoverEnterFrame(event:Event):void{ bmpd.lock(); bmpd.colorTransform(bmpd.rect, colorTransform); if (rows < portrait.height){ rows=rows + 2; } var y:int=0; while (y < portrait.height){ var x:int=0; while (x < portrait.width){ var p:Particle2D=particles[y][x] as Particle2D; if (!status[y][x]){ var px:Number=centerX + x; var py:Number=centerY + y; if (y < rows){ p.vx=(px - p.x) / (p.mass * 4); p.vy=(py - p.y) / (p.mass * 3); } p.update(); if (Math.abs(p.x - px) < 1 && Math.abs(p.y - py) < 1){ p.x=px; p.y=py; status[y][x]=true; } } bmpd.setPixel32(p.x, p.y, pixels[y][x]); x++; } y++; } bmpd.unlock(); } } }
initBitmap函数用来将嵌入的位图的BitmapData复制到指定的BitmapData中,并将指定的BitmapData添加到显示对象列表。使用双重while循环将位图的每一个像素的颜色存入pixels数组,将每一个像素的位置存入particles数组。
鼠标在舞台上首次单击则触发onBreakClick函数,该函数首先移除onBreakClick事件处理函数并重新添加onRecoverClick事件处理函数,用来响应还原图像的鼠标单击。再移除onRecoverEnterFrame事件处理函数并重新添加onBreakEnterFrame事件处理函数,用来逐帧更新显示,实现图片粒子化的效果。在双重while循环中指定particles数组中每一个粒子的速度。
在onBreakEnterFrame事件处理函数中,重复了实现BitmapData粒子的5个步骤。实现了粒子向上飞起的效果。
当再次在舞台上单击鼠标则执行onRecoverClick事件处理函数,同样切换必要的事件处理函数。即移除onRecoverClick事件处理函数并重新添加onBreakClick事件处理函数,用来响应图像粒子化的鼠标单击。再移除onBreakEnterFrame事件处理函数并重新添加onRecoverEnterFrame事件处理函数,用来逐帧更新显示,实现粒子还原成图片的效果。
在onRecoverEnterFrame事件处理函数中,除了使用实现BitmapData粒子动画的5个步骤之外,还定义了rows变量。每次递增该变量,使粒子逐渐还原成原始的图像,防止出现粒子在中途已经还原好图像的问题。