陈童的博客's Archivers

From everyinch on 2011-09-24 21:21:11

图片粒子动画

40000个BitmapData粒子的喷射动画很好地体现了BitmapData粒子在性能方面的优势,但视觉效果并不十分理想,能否使用BitmapData粒子实现更炫更复杂的视觉效果呢?下面就让我们模拟图片粒子化的动画效果。完成的效果是首先加载一张位图,单击鼠标后该位图粒子化并上升飞起,再次单击鼠标则向上飞起的粒子又重新生成初始的图片。类文件名为ParticleImage.as,源代码如下:
[code lang="as3"]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();
}
}
}[/code]
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变量。每次递增该变量,使粒子逐渐还原成原始的图像,防止出现粒子在中途已经还原好图像的问题。

<img class="alignnone size-medium wp-image-150" title="ParticleImage" src="http://www.everyinch.net/wp-content/uploads/2011/09/ParticleImage-300x247.jpg" alt="" width="300" height="247" />

查看完整版本: 图片粒子动画

Tags: 图片, 粒子


©陈童的博客