12.7.在Flex Effect里使用DisplacementMapFilter过滤器
12.7.1 问题
你需要创建一个在图片之间置换的渐变效果。
12.7.2 解决办法
继承TweenEffect 和TweenEffectInstance 类,创建一个具有最终置换值的TweenEffect 实例,然后将这些最终置换值传给它所创建的TweenEffectInstance 类实例。在自定义的TweenEffectInstance 类里,创建一个DisplacementMapFilter 对象并且使用Flex 框架的渐变引擎通过在每个onTweenUpdate 事件上生成新的过滤器来达到预期的置换值。
12.7.3 讨论
DisplacementMapFilter 通过使用另一张图片的象素点决定变形的位置和量来置换或者变形当前图片的像素点。
置换的位置和量到设定的像素是通过置换目标图片的像素点色值决定的。
DisplacementMapFilter 的构造方法如下:
-ActionScriptpublic?function?DisplacementMapFilter(mapBitmap:BitmapData?=?null,
mapPoint:Point?=?null,?componentX:uint?=?0,?componentY:uint?=?0,
scaleX:Number?=?0.0,?scaleY:Number?=?0.0,?mode:String?=?"wrap",
color:uint?=?0,?alpha:Number?=?0.0)
如此长的一行代码拆开理解较为简单:
-ActionScriptBitmapData?(default?=?null)
这是过滤器应用到的置换目标图片或组件所用到的BitmapData 对象。
mapPoint
这个是被过滤图片的位置,对应于过滤器要应用到的置换图片的左上角位置。如果仅仅过滤图片的一部分的话,可以使用这个参数。
componentX
该参数指定作用x 位置上的图片象素颜色通道。BitmapDataChannel 定义了所有有效的常量选项值:BitmapDataChannel.BLUE 或4, BitmapDataChannel.RED 或1,BitmapDataChannel.GREEN 或2, or BitmapDataChannel.ALPHA 或8。
componentY
指定作用y 位置上的图片象素颜色通道。取值范围和componentX 的相同。
scaleX
这个参数值指定X 轴方向上的置换强度。
scaleY
这个参数值指定Y 轴方向上的置换强度。
mode
这是一个字符串,它决定怎样处理置换像素后形成的空白空间。可选值申明为DisplacementMapFilterMode 类的常量,用以显示原始的像素(mode = IGNORE)、从图片另一边封装边缘像素点(mode = WRAP,默认值)、使用最近的置换像素(mode = CLAMP)或者使用某个颜色填充这些空间(mode = COLOR)。
CustomDisplacementEffect 例示CustomDisplacementInstance。如下:
-ActionScriptpackage?oreilly.cookbook{
import?mx.effects.IEffectInstance;
import?mx.effects.TweenEffect;
import?mx.events.EffectEvent;
public?class?CustomDisplacementEffect?extends?TweenEffect
{
public?var?image:Class;
public?var?yToDisplace:Number;
public?var?xToDisplace:Number;
public?function?CustomDisplacementEffect(target:Object=null)
{
super(target);
this.instanceClass?=?CustomDisplacementInstance;
}
override?protected?function
initInstance(instance:IEffectInstance):void?{
trace("?instance?initialized?");
super.initInstance(instance);
//?now?that?we've?instantiated?our?instance,?we?can?set
its?properties
CustomDisplacementInstance(instance).image?=?image;
CustomDisplacementInstance(instance).xToDisplace?=
this.xToDisplace;
CustomDisplacementInstance(instance).yToDisplace?=
this.yToDisplace;
}
override?public?function?getAffectedProperties():Array?{
trace("?return?all?the?target?properties?");
return?[];
}
}
}
实际上CustomDisplacementInstance 负责进行创建应用到目标的DisplacementEffect 对象。而位图对象,过滤器在DisplacementEffect 使用的,以及CustomDisplacementTween 的x 与y置换量都应用到该实例并传给DisplacementEffect。
CustomTweenEffect 创建CustomDisplacementInstance 的实例,如下:
-ActionScriptpackage?oreilly.cookbook{
import?flash.display.BitmapData;
import?flash.display.BitmapDataChannel;
import?flash.display.DisplayObject;
import?flash.filters.DisplacementMapFilter;
import?flash.filters.DisplacementMapFilterMode;
import?flash.geom.Point;
import?mx.effects.Tween;
import?mx.effects.effectClasses.TweenEffectInstance;
public?class?CustomDisplacementInstance?extends
TweenEffectInstance
{
public?var?image:Class;
public?var?xToDisplace:Number;
public?var?yToDisplace:Number;
public?var?filterMode:String?=
DisplacementMapFilterMode.WRAP;
private?var?filter:DisplacementMapFilter;
private?var?img:DisplayObject;
private?var?bmd:BitmapData;
public?function?CustomDisplacementInstance(target:Object)
{
super(target);
}
override?public?function?play():void?{
super.play();
//make?our?embedded?image?accessible?to?use
img?=?new?image();
bmd?=?new?BitmapData(img.width,?img.height,?true);
//draw?the?actual?byte?data?into?the?image
bmd.draw(img);
这个新过滤器被创建,将被设置初始状态的所有值:
-ActionScriptfilter?=?new?DisplacementMapFilter(bmd,?new
Point(DisplayObject(target).wi
dth/2?-?(img.width/2),?DisplayObject(target).height/2?-
(img.height/2))),
BitmapDataChannel.RED,?BitmapDataChannel.RED,?0,?0,
filterMode,?0.0,?1.0);
//copy?any?filters?already?exisiting?on?the?target?so
that?we?don't
destroy?them?when?we?add?our?new?filter
var?targetFilters:Array?=?(target?as
DisplayObject).filters;
targetFilters.push(filter);
//set?the?actual?filter?onto?the?target
(target?as?DisplayObject).filters?=?targetFilters;
//create?a?tween?that?will?begin?to?generate?the?next
values?of?each?frame?of?our?effect
this.tween?=?new?Tween(this,?[0,?0],?[xToDisplace,
yToDisplace],
duration);
}
该类的很多繁重工作都在setDisplacementFilter 中完成。因为过滤器是累积的(它们是叠加应用的),前面的DisplacementMapFilter 必须移除。这需要通过循环遍历目标对象的过滤器数组来完成,移除所有DisplacementMapFilter 的实例。然后使用Tween 传过来的值创建一个新的过滤器并且将此过滤器应用到目标对象上。注意要让过滤器适当的显示,过滤器数组必须要重置。使用Array.push 方法添加过滤器到数组中不会引起目标DisplayObject 使用过滤器重绘。
-ActionScriptprivate?function?setDisplacementFilter(displacement:Object):void{
var?filters:Array?=?target.filters;
//?Remove?any?existing?Displacement?filter?to?ensure?that
ours?is?the?only?one
var?n:int?=?filters.length;
for?(var?i:int?=?0;?i?<?n;?i++)?{
if?(filters[i]?is?DisplacementMapFilter)
filters.splice(i,?1);
}
//create?the?new?filter?with?the?values?passed?in?from
the?tween
filter?=?new?DisplacementMapFilter(bmd,?new?Point(0,?0),
BitmapDataChannel.RED,?BitmapDataChannel.RED,
displacement[0]?as?Number,?displacement[1]?as
Number,?filterMode,?0.0,?0);
//add?the?filter?to?the?filters?on?the?target
filters.push(filter);
target.filters?=?filters;
}
//each?time?we're?ready?to?update,?re-create?the
displacement?map?filter
override?public?function?onTweenUpdate(value:Object):void
{
setDisplacementFilter(value);
}
//set?the?filter?one?last?time?and?then?dispatch?the?tween
end?event
override?public?function?onTweenEnd(value:Object):void
{
setDisplacementFilter(value);
super.onTweenEnd(value);
}
}
}
当渐变结束时,DisplacementMapFilter 的最终值用来设置目标DisplayObject 的最终外观,同时调用TweenEffectInstance 类的onTweenEnd 方法。