创建和应用滤镜

Flash Player 9 和更高版本,Adobe AIR 1.0 和更高版本

使用滤镜可以对位图和显示对象应用从投影到斜角和模糊等各种效果。由于将每个滤镜定义为一个类,因此应用滤镜涉及创建滤镜对象的实例,这与构造任何其他对象并没有区别。创建了滤镜对象的实例后,通过使用该对象的 filters 属性可以很容易地将此实例应用于显示对象;如果是 BitmapData 对象,可以使用 applyFilter() 方法。

创建滤镜

若要创建滤镜对象,只需调用所选的滤镜类的构造函数方法即可。例如,若要创建 DropShadowFilter 对象,请使用以下代码:

import flash.filters.DropShadowFilter; 
var myFilter:DropShadowFilter = new DropShadowFilter();

虽然此处没有显示参数,但 DropShadowFilter() 构造函数(与所有滤镜类的构造函数一样)接受多个可用于自定义滤镜效果外观的可选参数。

应用滤镜

构造滤镜对象后,可以将其应用于显示对象或 BitmapData 对象;应用滤镜的方式取决于为之应用该滤镜的对象。

对显示对象应用滤镜

对显示对象应用滤镜效果时,可以通过 filters 属性应用这些效果。显示对象的 filters 属性是一个 Array 实例,其中的元素是应用于该显示对象的滤镜对象。若要对显示对象应用单个滤镜,请创建该滤镜实例,将其添加到 Array 实例,再将该 Array 对象分配给显示对象的 filters 属性:

import flash.display.Bitmap; 
import flash.display.BitmapData; 
import flash.filters.DropShadowFilter; 
 
// Create a bitmapData object and render it to screen 
var myBitmapData:BitmapData = new BitmapData(100,100,false,0xFFFF3300); 
var myDisplayObject:Bitmap = new Bitmap(myBitmapData); 
addChild(myDisplayObject); 
 
// Create a DropShadowFilter instance. 
var dropShadow:DropShadowFilter = new DropShadowFilter(); 
 
// Create the filters array, adding the filter to the array by passing it as  
// a parameter to the Array() constructor. 
var filtersArray:Array = new Array(dropShadow); 
 
// Assign the filters array to the display object to apply the filter. 
myDisplayObject.filters = filtersArray;

如果要为该对象分配多个滤镜,只需在将 Array 实例分配给 filters 属性之前将所有滤镜添加到该实例即可。可以通过将多个对象作为参数传递给 Array 的构造函数,将多个对象添加到 Array。例如,以下代码对上面创建的显示对象应用斜角滤镜和发光滤镜:

import flash.filters.BevelFilter; 
import flash.filters.GlowFilter; 
 
// Create the filters and add them to an array. 
var bevel:BevelFilter = new BevelFilter(); 
var glow:GlowFilter = new GlowFilter(); 
var filtersArray:Array = new Array(bevel, glow); 
 
// Assign the filters array to the display object to apply the filter. 
myDisplayObject.filters = filtersArray;

在创建包含滤镜的数组时,您可以使用 new Array() 构造函数创建该数组(如前面示例所示),也可以使用 Array 文本语法将滤镜括在中括号 ([]) 中。例如,下面这行代码:

var filters:Array = new Array(dropShadow, blur); 

与下面这行代码效果相同:

var filters:Array = [dropShadow, blur];

如果对显示对象应用多个滤镜,则会按顺序以累积方式应用这些滤镜。例如,如果滤镜数组有两个元素:先添加的斜角滤镜和后添加的投影滤镜,则投影滤镜既会应用于斜角滤镜,也会应用于显示对象。这是由于投影滤镜在滤镜数组中位于第二个位置。如果想要以非累积方式应用滤镜,可对显示对象的新副本应用每个滤镜。

如果只为显示对象分配一个或几个滤镜,则可以先创建该滤镜实例,然后在单个语句中将该实例分配给该对象。例如,下面的一行代码将模糊滤镜应用于名为 myDisplayObject 的显示对象:

myDisplayObject.filters = [new BlurFilter()];

上面的代码使用 Array 文本语法(中括号)创建一个 Array 实例,并创建一个 BlurFilter 实例作为 Array 中的一个元素,然后将该 Array 分配给名为 myDisplayObject 的显示对象的 filters 属性。

删除显示对象中的滤镜

删除显示对象中的所有滤镜非常简单,只需为 filters 属性分配一个 null 值即可:

myDisplayObject.filters = null;

如果您已对一个对象应用了多个滤镜,并且只想删除其中一个滤镜,则必须完成多个步骤才能更改 filters 属性数组。有关详细信息,请参阅使用滤镜的潜在问题

对 BitmapData 对象应用滤镜

对 BitmapData 对象应用滤镜需要使用 BitmapData 对象的 applyFilter() 方法:

var rect:Rectangle = new Rectangle(); 
var origin:Point = new Point(); 
myBitmapData.applyFilter(sourceBitmapData, rect, origin, new BlurFilter());

applyFilter() 方法会对源 BitmapData 对象应用滤镜,从而生成一个新的、应用滤镜的图像。此方法不会修改原始的源图像;而是将对源图像应用滤镜的结果存储在调用 applyFilter() 方法的 BitmapData 实例中。

滤镜的工作原理

显示对象过滤是通过将原始对象的副本缓存为透明位图来工作的。

将滤镜应用于显示对象后,只要此对象具有有效的滤镜列表,运行时会将该对象缓存为位图。然后,将此位图用作所有后续应用的滤镜效果的原始图像。

每个显示对象通常包含两个位图:一个包含原始未过滤的源显示对象,另一个用于过滤后的最终图像。呈示时使用最终图像。只要显示对象不发生更改,最终图像就不需要更新。

使用滤镜的潜在问题

使用滤镜时要记住几个导致混淆或问题的潜在根源。

滤镜和位图缓存

若要对显示对象应用滤镜,必须启用该对象的位图缓存。对其 cacheAsBitmap 属性设置为 false 的显示对象应用滤镜时,该对象的 cacheAsBitmap 属性会自动设置为 true。如果稍后删除了显示对象的所有滤镜,则 cacheAsBitmap 属性会重置为最后设置的值。

在运行时更改滤镜

如果显示对象已应用了一个或多个滤镜,则不能通过在 filters 属性数组中添加或删除滤镜来更改滤镜组。若要添加或更改要应用的滤镜组,必须对单独的数组进行更改,然后将该数组分配给显示对象的 filters 属性以对该对象应用滤镜。执行此操作最简单方法是将 filters 属性数组读入 Array 变量,然后对此临时数组进行修改。然后,将此数组重新分配回显示对象的 filters 属性。在较复杂的情况下,可能需要保留一个单独的主滤镜数组。可以对该主滤镜数组进行任何更改,并在每次更改后将该主数组重新分配给显示对象的 filters 属性。

添加其他滤镜

下面的代码演示向已应用一个或多个滤镜的显示对象添加其他滤镜的过程。首先,对名为 myDisplayObject 的显示对象应用发光滤镜,然后,在单击该显示对象时,调用 addFilters() 函数。在此函数中,另有两个滤镜应用于 myDisplayObject

import flash.events.MouseEvent; 
import flash.filters.*; 
 
myDisplayObject.filters = [new GlowFilter()]; 
 
function addFilters(event:MouseEvent):void 
{ 
    // Make a copy of the filters array. 
    var filtersCopy:Array = myDisplayObject.filters; 
 
    // Make desired changes to the filters (in this case, adding filters). 
    filtersCopy.push(new BlurFilter()); 
    filtersCopy.push(new DropShadowFilter()); 
 
    // Apply the changes by reassigning the array to the filters property. 
    myDisplayObject.filters = filtersCopy; 
} 
 
myDisplayObject.addEventListener(MouseEvent.CLICK, addFilters);

从一组滤镜中删除一个滤镜

如果显示对象已经应用了多个滤镜,您希望删除其中的一个滤镜并希望其他滤镜继续应用于该对象,请将这些滤镜复制到一个临时数组中,从该数组中删除不需要的滤镜,然后将该临时数组重新分配给该显示对象的 filters 属性。检索值和删除数组元素一节介绍了从任意数组删除一个或多个元素的多种方法。

最简单的情况是删除对象上最顶层的滤镜(最后一个应用于对象的滤镜)。可以使用 Array 类的 pop() 方法从数组中删除滤镜:

// Example of removing the top-most filter from a display object  
// named "filteredObject". 
 
var tempFilters:Array = filteredObject.filters; 
 
// Remove the last element from the Array (the top-most filter). 
tempFilters.pop(); 
 
// Apply the new set of filters to the display object. 
filteredObject.filters = tempFilters;

同样,若要删除最底层的滤镜(第一个应用于对象的滤镜),也可以使用相同的代码,不过需要用 Array 类的 shift() 方法替换 pop() 方法。

若要从滤镜数组的中间删除滤镜(假定该数组有两个以上的滤镜),则可以使用 splice() 方法。您必须知道要删除的滤镜的索引(在数组中的位置)。例如,下面的代码从显示对象中删除第二个滤镜(索引 1 处的滤镜):

// Example of removing a filter from the middle of a stack of filters 
// applied to a display object named "filteredObject". 
 
var tempFilters:Array = filteredObject.filters; 
 
// Remove the second filter from the array. It's the item at index 1  
// because Array indexes start from 0. 
// The first "1" indicates the index of the filter to remove; the  
// second "1" indicates how many elements to remove. 
tempFilters.splice(1, 1); 
 
// Apply the new set of filters to the display object. 
filteredObject.filters = tempFilters;

确定滤镜索引

您需要知道要从数组中删除哪个滤镜,才能确定该滤镜的索引。您必须知道(根据应用程序的设计方式)或计算出要删除的滤镜的索引。

最佳方法是通过设计应用程序,确保要删除的滤镜始终位于滤镜组中的同一位置。例如,如果有一个显示对象先后应用了卷积滤镜和投影滤镜,您希望删除投影滤镜而保留卷积滤镜,由于滤镜位于已知位置(最顶层的滤镜),因此您可以提前知道应使用哪种 Array 方法(在本例中,使用 Array.pop() 删除投影滤镜)。

如果您要删除的滤镜始终属于某个特定类型,但不一定总在滤镜组中的同一位置,那么您可以检查该数组中每个滤镜的数据类型,从而确定将要删除哪个滤镜。例如,下面的代码确定滤镜组中的哪个滤镜是发光滤镜,然后从该组中删除该滤镜。

// Example of removing a glow filter from a set of filters, where the 
//filter you want to remove is the only GlowFilter instance applied  
// to the filtered object. 
 
var tempFilters:Array = filteredObject.filters; 
 
// Loop through the filters to find the index of the GlowFilter instance. 
var glowIndex:int; 
var numFilters:int = tempFilters.length; 
for (var i:int = 0; i < numFilters; i++) 
{ 
    if (tempFilters[i] is GlowFilter) 
    { 
        glowIndex = i; 
        break; 
    } 
} 
 
// Remove the glow filter from the array. 
tempFilters.splice(glowIndex, 1); 
 
// Apply the new set of filters to the display object. 
filteredObject.filters = tempFilters;

在更为复杂的情况下(比如要删除的滤镜是在运行时选定的),最好为滤镜数组保留一个单独的永久副本,用作主滤镜列表。不论何时对滤镜组进行更改,均需更改主列表,然后按照显示对象的 filters 属性应用该滤镜数组。

例如,在下面的代码清单中,多个卷积滤镜应用于一个显示对象,以制造不同的视觉效果,随后在应用程序中删除其中一个滤镜而保留其余滤镜。在本例中,代码保留滤镜数组的主副本以及对于要删除的滤镜的引用。查找并删除特定滤镜的方法与前面的方法类似,但不是创建滤镜数组的临时副本,而是操作主副本,然后将其应用于显示对象。

// Example of removing a filter from a set of  
// filters, where there may be more than one  
// of that type of filter applied to the filtered  
// object, and you only want to remove one. 
 
// A master list of filters is stored in a separate, 
// persistent Array variable. 
var masterFilterList:Array; 
 
// At some point, you store a reference to the filter you 
// want to remove. 
var filterToRemove:ConvolutionFilter; 
 
// ... assume the filters have been added to masterFilterList, 
// which is then assigned as the filteredObject.filters: 
filteredObject.filters = masterFilterList; 
 
// ... later, when it's time to remove the filter, this code gets called: 
 
// Loop through the filters to find the index of masterFilterList. 
var removeIndex:int = -1; 
var numFilters:int = masterFilterList.length; 
for (var i:int = 0; i < numFilters; i++) 
{ 
    if (masterFilterList[i] == filterToRemove) 
    { 
        removeIndex = i; 
        break; 
    } 
} 
 
if (removeIndex >= 0) 
{ 
    // Remove the filter from the array. 
    masterFilterList.splice(removeIndex, 1); 
 
    // Apply the new set of filters to the display object. 
    filteredObject.filters = masterFilterList; 
}

在此方法(将存储的滤镜引用与滤镜数组中的项目进行比较以确定要删除哪个滤镜)中,必须 为滤镜数组保留一个单独的副本 — 如果将存储的滤镜引用与从显示对象的 filters 属性复制的临时数组中的元素进行比较,则代码将不起作用。这是因为在内部将数组分配给 filters 属性时,运行时会为数组中的每个滤镜对象创建副本。这些副本(而非原始对象)将应用于显示对象,而在将 filters 属性读入临时数组时,临时数组包含对复制的滤镜对象的引用,而不是对原始滤镜对象的引用。因此,如果在上一示例中尝试通过将 filterToRemove 与临时滤镜数组中的滤镜进行比较来确定其索引,则不会找到匹配的结果。

滤镜和对象变形

显示对象的边框矩形之外的任何过滤区域(例如,投影)都不能视为可进行点击检测(确定实例是否与其他实例重叠或交叉)的表面。由于 DisplayObject 类的点击检测方法是基于矢量的,因此无法对位图结果执行点击检测。例如,如果您对按钮实例应用斜角滤镜,则在该实例的斜角部分,点击检测不可用。

滤镜不支持缩放、旋转和倾斜;如果过滤的显示对象本身进行了缩放(如果 scaleXscaleY 不是 100%),则滤镜效果将不随该实例缩放。这意味着,实例的原始形状将旋转、缩放或倾斜;而滤镜不随实例一起旋转、缩放或倾斜。

可以使用滤镜给实例添加动画,以形成理想的效果,或者嵌套实例并使用 BitmapData 类使滤镜动起来,以获得此效果。

滤镜和位图对象

对 BitmapData 对象应用滤镜时,cacheAsBitmap 属性会自动设置为 true。通过这种方式,滤镜实际上是应用于对象的副本而不是原始对象。

之后,会将此副本放在主显示(原始对象)上,尽量接近最近的像素。如果原始位图的边框发生更改,则会从头重新创建过滤的副本位图,而不是被伸展或扭曲。

如果清除了显示对象的所有滤镜,cacheAsBitmap 属性会重置为应用滤镜之前的值。