复制图形概念上理解起来很简单,但是他是drawing API工具包新增功能里最受欢迎的改变之一。 任何绘制在shape和Sprite里面的图形都与该shape或sprite融合在一起,没有任何途径从一个图形对象中复制图形到另外一个中,当然你可以使用BitmapData类来复制图形,但是它复制的是位图而不是矢量图,或者你可以保存所有的绘图过程用来重新创建图像,显然这不是个十分灵活的解决方案。
现在,你只需要一个简单的函数就能搞定:
copyGraphics():void
使用copyGraphics(),一个图形实例的任何内容可以随时复制到另外一个。实在太爽了,我们将通过下面的例子探讨它可能的用途,创建一个类似于老旧的呼吸运动记录器的东西,如同http://www.myoats.com上的一样,图1-13展示了www.myoats.com上的效果。
下面的类你可以在CopyingGraphics.as文件中找到,编译并测试这个类,你会看到一个黑色的舞台,当你点击鼠标并且移动来绘制线条的时候,会整个旋转你绘制的图形,线条会被围绕舞台中心复制多次并且跟随你所画的更新。使用上下箭头,你可以调整复制的次数,结果如图1-14所示:
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/fjc/)
与先前的例子比较,这是一个漂亮的大工程,所以仔细看看代码,玩玩最终生成的程序,之后我会详细讲解有关内容。
代码:
package{ import flash.display.Shape; import flash.display.Sprite; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.filters.GlowFilter; import flash.ui.Keyboard; /** * ... * @author bardpub */ [SWF(width=550,height=400,backgroundColor=0)] public class CopyingGraphics extends Sprite { private static const INIT_SEGMENTS:int = 10; private static const MAX_SEGMENTS:int = 20; private static const MIN_SEGMENTS:int = 3; private static const THICKNESS:int = 1; private static const COLOR:uint = 0x66CCCC; private static const ROTATION_RATE:int = 1; private var _shapeHolder:Sprite; private var _shapes:Vector.; public function CopyingGraphics() { init(); } private function init():void { _shapeHolder = new Sprite(); _shapeHolder.x = stage.stageWidth / 2; _shapeHolder.y = stage.stageHeight / 2; addChild(_shapeHolder); _shapes = new Vector.(); for (var i:int = 0; i < INIT_SEGMENTS; i++ ) { addSegment(); } positionSegments(); stage.addEventListener(MouseEvent.MOUSE_DOWN, onStageMouseDown); stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp); stage.addEventListener(KeyboardEvent.KEY_DOWN, onStageKeyDown); filters = [new GlowFilter(COLOR)]; } private function draw():void { var shape:Shape = _shapes[0]; shape.graphics.lineTo(shape.mouseX, shape.mouseY); var segments:int = _shapeHolder.numChildren; for (var i:int = 1; i < segments; i++ ) { _shapes[i].graphics.copyFrom(shape.graphics); } } private function addSegment():void { var shape:Shape = new Shape(); if (_shapes.length 0) { shape.graphics.copyFrom(_shapes[0].graphics); } else { shape.graphics.lineStyle(THICKNESS, COLOR); } _shapes.push(shape); _shapeHolder.addChild(shape); } private function removeSegment():void { var shape:Shape = _shapes.pop(); _shapeHolder.removeChild(shape); } private function positionSegments():void { var segments:int = _shapeHolder.numChildren; var angle:Number = 360 / segments; for (var i:int = 1; i < segments; i++ ) { _shapes[i].rotation = angle * i; } } private function onStageMouseDown(event:MouseEvent):void { var shape:Shape = _shapes[0]; shape.graphics.moveTo(shape.mouseX, shape.mouseY); addEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onStageMouseUp(event:MouseEvent):void { removeEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onEnterFrame(event:Event):void { _shapeHolder.rotation += ROTATION_RATE; draw(); } private function onStageKeyDown(event:KeyboardEvent):void { switch (event.keyCode) { case Keyboard.UP: if (_shapeHolder.numChildren < MAX_SEGMENTS) { addSegment(); positionSegments(); } break; case Keyboard.DOWN: if (_shapeHolder.numChildren MIN_SEGMENTS) { removeSegment(); positionSegments(); } break; } } }}12AS3.0 Image Effects 复制图形(下)
在声明了包与类并且导入了相关的类之后,我定义了一些在程序中会用到的常量,(注:这段程序会在一个shape内绘制内容,然后会复制几个shape并让他旋转一定角度,作者把这些shape称为段(segment),把我弄得晕晕的,总让我想起汇编的segment ) INIT_SEGMENTS是开始时围绕所绘制的线条旋转的段的个数,MAX_SEGEMNTS和MIN_SEGMENTS 就是最大值与最小值了, THICKNESS与COLOR用来定义线条的外观,最后,ROTATION_RATE用来控制当你绘制的时候,shape旋转得多快。
随后的一些属性用来保持程序中创建的shape和Sprite的引用, _shapeHolder是一个大容器,所有的shape添加在他里面,他会在你鼠标绘制的时候旋转, _shapes向量数组存贮所有创建的shape实例。
引用:ctionScript3.0中的Vector类型是Flash Player10新增的内容,是一个与Array相似的数据结构,可以使用任何你知道的有关数组的方法操作,但是有一套更为严格的规则,最明显的是 Vector在创建的时候需要指定一个参数类型,并且它只接受指定参数类型的数据,如下面的一句代码:
var sprites:Vector.<Sprite = new Vector.<Sprite();
你只能把Sprite类的实例放入数组,不像Array一样你可以放任何类型的数据,这样做最大的好处就是任何被添加到Vector里的数据你都明确知道它的类型,并不需要从Object类型转换,从Vector里取出来的数据并不需要做类型检查。
var sprite:Sprite = sprites.pop()
如果你试图在一个Array类型上这么错,编译器会抛出一个错误,Vector让代码更加干净,执行速度更快。
在类构造器内,我只是调用了init()方法。通常,当我需要超过两到三行代码来初始化程序的时候,我会把它们放在一个名为init的方法内,然后在构造器中调用。
init方法内我做的第一件事是创建_shapeHolder,把他定位在舞台的中间并且添加到显示列表。
代码:
_shapeHolder = new Sprite();_shapeHolder.x = stage.stageWidth / 2;_shapeHolder.y = stage.stageHeight / 2;addChild(_shapeHolder);
然后我初始化_shapes向量数组,用一个循环调用addSegment()方法创建所有的段。addSegment()方法是用来添加一个段到 _shapeHolder容器的。
循环结束后调用positionSegment()方法来围绕中心旋转所有的段。
代码:
_shapes = new Vector.<Shape();for (var i:int = 0; i < INIT_SEGMENTS; i++ ) { addSegment();}positionSegments();
现在我们来看看addSegment()方法。
代码:
var shape:Shape = new Shape();if (_shapes.length 0) { shape.graphics.copyFrom(_shapes[0].graphics);} else { shape.graphics.lineStyle(THICKNESS, COLOR);}_shapes.push(shape);_shapeHolder.addChild(shape);
创建一个新的Shape实例之后,我检查一下他是不是第一个被创建的,如果不是第一个我要用来绘图的shape,我调用神奇的copyFrom()方法,这会把我在第一个shape中绘制的所有的内容复制到现在的shape,如果是第一个shape,我就调用 lineStyle()方法定义绘制的线条样式,最后,我把新创建的shape添加到_shapes数组同时添加进_shapesHolder容器。
所有的段都添加好了之后,调用positionSegments()方法来围绕_shapesHolder容器的注册点也就是舞台的中心点旋转他们,用360除以段的数量就可以很容易的算出每个段之间相距的角度。代码:
var segments:int = _shapeHolder.numChildren;var angle:Number = 360 / segments;for (var i:int = 1; i < segments; i++ ) { _shapes[i].rotation = angle * i;}
init方法最后的几行为舞台添加3个事件侦听器,然后给舞台应用发光滤镜用来添加一些视觉效果。(有关滤镜的更多内容在下一章)。
在onStageMouseDown()函数中,我在第一个shape中调用moveTo()方法,这样虚拟画笔会移动到鼠标当前的位置,然后我创建一个侦听器用来侦听EVENT_FRAME事件。
代码:
var shape:Shape = _shapes[0];shape.graphics.moveTo(shape.mouseX, shape.mouseY);addEventListener(Event.ENTER_FRAME, onEnterFrame);
onStageMouseUp()函数中我只是简单的移除对ENTER_FRAME事件的侦听,onEnterFrame函数会在ENTER_FRAME 发生时调用。在onEnterFrame里面,我旋转_shapeHolder容器并调用draw()方法。
代码:
_shapeHolder.rotation += ROTATION_RATE;draw();
draw()方法,我在第一个shape中调用lineTo方法,这样会绘制一条到鼠标当前位置的直线。
代码:
var shape:Shape = _shapes[0];shape.graphics.lineTo(shape.mouseX, shape.mouseY);
记住当鼠标按下之后这会发生在每一帧上,即便鼠标不移动,但是因为_shapeHolder容器自身的旋转,所以当前鼠标坐在的位置跟之前 shape.moveTo定义的位置还是不一样。接下来,我遍历所有的段,调用copyFrom方法把第一个shape中的内容复制到他们每一个里面,就这么简单!
代码:
var segments:int = _shapeHolder.numChildren;for (var i:int = 1; i < segments; i++ ) { _shapes[i].graphics.copyFrom(shape.graphics);}
init方法里面最后一个侦听器用来侦听键盘按下事件。当键盘按下时,onStageKeyDown方法被调用,在它里面我检查上下箭头是否被按下,如果按下Up键,我首先确保当前段的数量小于最大段数限制,如果还没达到限制,我调用addSegment()方法添加一个新段,然后调用 positionSegment()方法定位所有的段。
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/fjc/)代码:
case Keyboard.UP: if (_shapeHolder.numChildren < MAX_SEGMENTS) { addSegment(); positionSegments(); }
如果DOWN键被按下,我检查当前段数是否小于最小段数限制,如果没有,我调用removeSegment()方法删除一个段并调用 positionSegment()方法从新定位所有的段。
removeSegment()方法简单的删除_shape数组里面的最后一个值,并把它从_shapeHolder容器中删除。
代码:
var shape:Shape = _shapes.pop();_shapeHolder.removeChild(shape);
现在继续玩玩你的大作吧,看看你画出什么漂亮的东西出来,记得按上下键来调整段的数量,看看出来的效果有什么不同。图1-15中我展示了一些我的“艺术品”。
12