原文地址:http://bbs.9c9t.com/thread-196141-1-1.html
FLASH AS3 滤镜(Filter)
滤镜(Filter) 滤镜是一些位图的效果,可以应用于任何显示对象。在 Flash IDE 中可以通过滤镜面 板或使用时间轴的 ActionScript 来使用滤镜,由于这本书是关于 ActionScript 的,所以 只能简朴地讨论一下应用滤镜的方法。在 AS 3 中包括以下几种滤镜: ■ Drop shadow(投影滤镜) ■ Blur(模糊滤镜) ■ Glow(发光滤镜) ■ Bevel(斜角滤镜) ■ Gradient bevel(渐变斜角滤镜) ■ Gradient glow(渐变发光滤镜) ■ Color matrix(颜色矩阵滤镜) ■ Convolution(卷积滤镜) Displacement map(置换图滤镜) 虽然不能一一介绍每种滤镜的使用细节,但大家可以通过帮助文档来学习。在书中还会 有很多滤镜使用的例子,所以在这里只介绍一下滤镜使用的总体方法和两个具体实例。 创建滤镜 通过使用 new 关键字及滤镜名来创建滤镜,并给出所需的参数。例如,创建一个 blur filter(模糊滤镜),最简朴的一种滤镜,写法入下: var blur:BlurFilter = new BlurFilter(5, 5, 3); 参数分别为 blurX,blurY,quality。这个例子会将对象在 x和 y 轴上模糊 5 个像素,模糊 的品质为中等。 另一点需要知道的是滤镜在其名为 flash.filters 的包中。所以要在文件的开始处将它们 导入进来: import flash.filters.BlurFilter; 如果希望导入包中所有的滤镜,可以使用简写: import flash.filters.*; 69 现在,我们可以直接创建任何类型的滤镜了,但是一般来说,除非要使用这个包中的大 部分滤镜,否则最好避免使用通配符(*),而是明确地导入所需要的类。这样做只是为了能 够清楚,哪些是真正想要导入的而哪些不是。好了,现在已经创建了一个模糊滤镜,但怎样 才能使它去模糊一个对象呢? 任何一个显示对象都有一个名为 filters 的属性,这是一个包括了所有滤镜的数组, 因为如果一个对象要应用多个滤镜,那么只需要再将模糊滤镜放到数组中即可。乐观地看, 应用滤镜应该可以像使用基本数组操作那样简朴,push,就像这样 mySprite.filters.push(blur);,但是很遗憾,没有这么简朴。在整个数组赋值为 filters 之前,Flash 不关心 filters 数组的变化。 如果已知对象没有应用任何的滤镜,或想要重写它们,只需要孝蕤一个数组,将我们的 滤镜粘在上面,再将这个新数组赋给 filters 属性就可以了。先来试一下,下面一个文档 类 Filters.as,创建了一个 sprite 影片并且在里面绘制了一个黄色的正方形,然后,创 建一个滤镜,加入数组中,最后将数组赋给 sprite 的 filters 属性: package { import flash.display.Sprite; import flash.filters.BlurFilter; public class Filters extends Sprite { public function Filters() { init(); } private function init():void { var sprite:Sprite = new Sprite(); sprite.graphics.lineStyle(2); sprite.graphics.beginFill(0xffff00); sprite.graphics.drawRect(100, 100, 100, 100); sprite.graphics.endFill(); addChild(sprite); var blur:BlurFilter = new BlurFilter(5, 5, 3); var filters:Array = new Array(); filters.push(blur); sprite.filters = filters; } } } 瞧!出现了一个模糊的黄色方块儿。重要的部分用黑体着重,我们可以简写一点: var blur:BlurFilter = new BlurFilter(5, 5, 3); var filters:Array = [blur]; sprite.filters = filters; 或再短一点: sprite.filters = [new BlurFilter(5, 5, 3)]; 在创建数组的同时,将滤镜放进去,并应用 filters 属性,这样一来,Flash 会很高 兴。 但是如果已经有了滤镜并希望继续使用,这时,但又不确定是否有滤镜存在,那该怎样 办呢?在 Flash 8 中,这是件很麻烦的事,因为一个显示对象的 filters 属性如果没有应 用滤镜,那么它将是未定义(undefined)的。但在 AS 3 中, filters 数组总是保持为一个 空数组,只需要给数组赋值,将滤镜 push 进去,并将其赋给对象的 filters 属性即可, 方法如下: var filters:Array = sprite.filters; filters.push(new BlurFilter(5, 5, 3)); 70 sprite.filters = filters; 如果使用这种方法,那么无论是否有滤镜存在都没有问题,滤镜只是被加入到数组列表 中而已。因为 filters 属性是一套成熟的数组,所以可以使用不同的数组操作方法。比如, 使用 concat 方法: sprite.filters = sprite.filters.concat(new BlurFilter(5, 5, 3)); 我不认为这是个“正娶诒的做法,大家只要知道将一个包涵有滤镜的数组赋给 filters 属性就足够了。 动态滤镜 现在我们已经基本上知道了如何在 ActionScript 中使用滤镜了。接下来,用已经学过的知 识,制作一个动态滤镜。这个效果,使用文档类 AnimatedFilters.as: package { import flash.display.Sprite; import flash.events.Event; import flash.filters.DropShadowFilter; public class AnimatedFilters extends Sprite { private var filterropShadowFilter; private var sprite:Sprite; public function AnimatedFilters() { init(); } private function init():void { sprite = new Sprite(); sprite.graphics.lineStyle(2); sprite.graphics.beginFill(0xffff00); sprite.graphics.drawRect(-50, -50, 100, 100); sprite.graphics.endFill(); sprite.x = 200; sprite.y = 200; addChild(sprite); filter = new DropShadowFilter(0, 0, 0, 1, 20, 20, .3); addEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onEnterFrame(event:Event):void { var dx:Number = mouseX - sprite.x; var dy:Number = mouseY - sprite.y; filter.distance = -Math.sqrt(dx * dx + dy * dy) / 10; filter.angle = Math.atan2(dy, dx) * 180 / Math.PI; sprite.filters = [filter]; } } } 首先在 sprite 中画一个正方形,正方形在 sprite 的居中位置,然后将 sprite 移动 到舞台中间,用一些默认属性创建投影滤镜(DropShadowFilter)。 添加一个 enterFrame 事件的侦听器及处理函数: onEnterFrame 方法, 用于计算角度(angle) 及使用三角函数计算 sprite 影片与鼠标的距离(distance)。使用 angle 和 distance 设 置投影滤镜的 angle 和 distance 属性,最后将这个滤镜再应用到 sprite 上。请注意, 71 我们不需要每次都创建一个新的滤镜,可以继续使用同一个滤镜,只需要改变它的属性即可。 然而,只是改变这些属性也不能更新 sprite 影片的显示。因此,还需要再将变化过的滤镜 效果赋值给 filters 属性。 位图(Bitmaps) 与滤镜相同,可以用整本书来介绍 Bitmap 和 BitmapData 类,看起来也不错,但是这并不 是本书的目的。我们将通过一些简朴的例子,用来指出 AS 2 与 AS 3 中位图处理的变化。 在 AS 2 中,通过调用 BitmapData()函数,孝蕤一个 BitmapData 对象使用如下参数: new BitmapData (width:Number, height:Number, transparent:Boolean, fillColor:Number) 你也许猜到了, BitmapData 类同样也是嵌入在一个包中,完整的使用名称如下 flash.display.BitmapData。所以需要导入包,对于 width 和 height 参数则非常显而易 见, transparent 参数表示创建的图像是否包涵一个 alpha 通道,选择 true 或 false , fillColor 是创建图像的初始颜色, 如果 transparent 为 true 的话, 那么位图就用 32 位 色表示,0xAARRGGBB,如果为 false 的话,就可以使用 24 位安全色表示。 在创建 BitmapData 对象时,也许很想能看到它的样子。在 AS 2 中,使用 attachBitmap 命令在影片剪辑中添加一个位图。大家也许会想,现在是否可以使用 addChild 在显示对象中添加一个位图,但事实上并没有这么简朴。问题在于 addChild 只 对继承自 DisplayObject 类的对象起作用,如 Sprite 影片,影片剪辑和文本框。然而, 如果我们研究一下类的结构,就会发现 BitmapData ,没有继承自 DisplayObject,所有不 能直接添加对象。这就是为什么要有 Bitmap 类的原因, Bitmap 类几乎始终都有一个函数 作为 BitmapData 实例的容器,可以这样创建: var myBitmapData:BitmapData = new BitmapData(100, 100, false, 0xff0000); var myBitmap:Bitmap = new Bitmap(myBitmapData); 现在就可以将对象加入到显示列表了: addChild(myBitmap); 使其可见后,Bitmap 实例也可以改变位置,进行缩放,增加滤镜等等。 测试这个例子,只需要在第二章给出的类框架的 init 方法加入这三行就可以了,不要 忘记导入 flash.display.Bitmap 和 flash.display.BitmapData,运行后就会看到一个红 色的正方形。乍看上去,与使用绘图 API 所画的图形没什么不同,但是要知道这并不是矢 量图绘制法:填充一个红色的正方形。这是张位图图像,在位图中每一个像素都要分别指定 而且是可变的。事实上,每一个像素值都可以使用 getPixel,getPixel32 和 setPixel,setPixel32 进行读取和设置。两个版本的不同之处在于 getPixel 和 setPixel 使用 24 位色彩值忽略了 alpha 通道,而 “32”版的则使用 32 位色彩值其中包括了透明 度信息。让我们来做个例子,制作一个简朴的喷漆工具,就像所有位图喷漆程序一样。 这里是文档类,SprayPaint.as: package { import flash.display.Sprite; import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.MouseEvent; import flash.events.Event; import flash.filters.BlurFilter; public class SprayPaint extends Sprite { private var canvas:BitmapData; 72 private var color:uint; private var size:Number = 50; private var density:Number = 50; public function SprayPaint() { init(); } private function init():void { canvas = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0x00000000); var bmp:Bitmap = new Bitmap(canvas); addChild(bmp); stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); } private function onMouseDown(event:MouseEvent):void { color = Math.random() * 0xffffff + 0xff000000; addEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onMouseUp(event:MouseEvent):void { removeEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onEnterFrame(event:Event):void { for (var i:int = 0; i < density; i++) { var angle:Number = Math.random() * Math.PI * 2; var radius:Number = Math.random() * size; var xpos:Number = mouseX + Math.cos(angle) * radius; var ypos:Number = mouseY + Math.sin(angle) * radius; canvas.setPixel32(xpos, ypos, color); } } } } 这也许是目前为止最复杂的代码,但除了 BitmapData 的内容外,其它的知识前面都讲 过,只不过又使用了一遍而已。一步步来看,首先,创建了一些类变量,包括 canvas 变量, 用于存放 BitmapData 的实例。创建的实例尺寸等于舞台的尺寸,并使用透明的背景色。然 后使用 canvas 创建一个位图,并加入到显示列表。 鼠标事件处理函数中选择了一个随机的颜色,并且带有添加和删除 enterFrame 事件处理函 数的功能。我们来回忆一下三角学,首先,从 0 到 Math.PI * 2 中计算出一个随机的角度, 不要忘记使用弧度制表示,相当于随机的 360度。然后,计算出一个随机的半径后,再使用 三角函数将半径和角度转换为 x,y 值。最后使用 setPixel32 以鼠标位置加上随机的 x,y 值的像素点设置为喷漆色,每一次开始喷漆时随机决定颜色。在这个例子中有一个 for 循 环,每一帧都会进行循环,每次循环多少次由 density 的值决定。 color 的值为24 位的 色彩值,然后加上 0xFF000000,为的是设置 alpha 通道为完全不透明,如果没有加上这个 值,那么所有的颜色就都为透明的。如果用 0xFFFFFFFF 乘以 Math.random(),那么颜色的 透明度是随机的,也许是你想要的,但不是我想要的。通过改变 density 和 size 的值再 测试一下,看看会有些什么不同的效果。大家也许已经想到如何让用户来控制改变这些参数 了。 刚刚看到这个程序时,你也许会想,“真是小题大作,完全可以用绘图 API 或通过加 73 载小影片剪辑并改变颜色来实现”。是的,完成可以这么做,但是如果使用绘图 API 绘出 成千上万的独立图像后,会发习画得越多,速度越慢。画过几百个图形后,慢下来的速度会 变得非常明显,这个程序也就费掉了,使用加载影片剪辑的方式也是如此。但是,使用位图 就完全不同了,我们可以使用这个程序喷上一天,都不影响程序的速度或效率。 如果想看到更酷的效果,就把下面一行代码加在位图对象 bmp 的后面: bmp.filters = [new BlurFilter(2, 2, 3)]; 在位图中使用模糊滤镜比在矢量图中使用效果更加明显。 当然, 设置像素是 BitmapData 对象能做的最简朴的操作之一。除了获取和设置像素,BitmapData 对象还有其它二十多种 方法,这些方法可用来复制像素,设置阈值,分解,合并,滚动,等等。我个人最喜欢的一 个是 perlinenoise 方法,该函数允许我们创建一个随机的有组织的图案。对于制造烟,云 和水波纹效果都非常有用。有兴趣的话大家可以试验一下。 |