使用ActionScript 3的绘图命令

Adobe Flash Player 10(及其后续版本)为ActionScript中的程序化绘图功能提供了一组扩展的显示类支持。在这些新添图形类的帮助下,您现在有两种途径可以进行图形脚本的编辑:

  • 1.基本的点到点式的命令集,用于绘制围绕图形边沿的线条以及填充图形。
  • 2.一种新的,高级的绘图命令集,允许接受绘图属性的Vector(向量)对象(数组类型)作为参数

如果您感兴趣于使用有限数量的坐标系快速编辑简单图形,请使用flash.display.Graphics类中的基本绘图命令。 如果您计划使用许多坐标点来创建一个较为复杂的图形,或者想要通过编程方式来快速改变图形的点、颜色填充或者笔划的属性,亦或打算将成型的图形及其属性应 用到其它图形中,那么请使用更高级的图形数据类和命令。

本文的焦点在于高级绘图API,但我们还是应该先看一个基本绘图API的例子,这对我们理解整个绘图API的发展历程很有帮助。

这篇文章并没有介绍到程序中能应用到的所有ActionScript类。如果想了解更多信息,请参阅ActionScript 3.0 Reference中的Adobe Flash Platform(Adobe Flash开发平台)以及ActionScript 3.0 Developer’s Guide(ActionScript 3.0 开发者指南)中的Using the drawing API(使用绘图API) 章节。

基本绘图命令

ActionScript 3通过使用Graphics 类,为我们提供了绘制直线和曲线功能。Graphics 类中的基本命令允许您定义线条类型,以及在一系列的点上移动线条,还有为图形填充颜色等。

示例


      //定义线条类型
      graphics.lineStyle(2,0x000000); 
      //定义填充色
      graphics.beginFill(0x666699) ;
      //设置线条起点
      graphics.moveTo(10,10);
      //通过一系列坐标点移动线条
      graphics.lineTo(10,100);
      graphics.lineTo(100,100);
      graphics.lineTo(100,10);
      graphics.lineTo(10,10)


结果

请注意: 即使脚本将graphics.lineTo(10,10) 这条命令注释掉了,但是此图形仍然可以封闭并填充色彩。

Graphics 类也包括了一些用于渐变填充、位图填充、线条类型,以及比如圆和椭圆等特殊图形的API。但是如果您想绘制一个拥有许多坐标点的极其复杂的图形时,该怎么 办呢? 或许您需要定义一个笔划或者填充色彩以供您多次应用到同一项目中不同部分的图形中,亦或需要图形能够随着从函数或用户交互输入的数据动态地变化。您会看 到,如果使用一长串的lineTo()命令以及精确的起始结束笔划和填充设置,绘制过程将会变得很难处理。所以,现在flash.display包中已包 含了一些新类,允许您创建绘图数据对象并将其作为参数传递给一些新的Graphics 类命令。

高级绘图命令

在基本绘图API中,您设置了线条类型和填充类型,同时开始使用一条条的命令来绘制图形直至最终成型。 在高级绘图API的帮助下,您可以建立用来描绘一个图形的所有数据(坐标、线条属性、填充属性等),然后使用单条绘图命令来处理数据和描绘图形。 这种数据驱动型的绘图API基于以下三个组成块:

  • 1.绘图数据: 像Graphics.lineTo()这样的绘图命令现在在 GraphicsPathCommand类中被表示为常量(0~5)。您可以在一个数组——即被称为向量对象的类型数组——中存储一系列这样的常量。向量 对象允许您将一组单一类型的数据捆绑到一起。然后,您就可以使用这些向量作为绘图命令的参数来描绘一个图形了。
  • 2.绘图类型数据: 绘图类型数据包括所有用来确定线条(笔划)、填充或轨迹的外形的属性集合,这些属性集合通 过使用那些执行flash.display.IGraphicsData界面的类来确定上述的外形。执行了IGraphicsData 界面的类可以创建含有将要用到的笔划、填充或轨迹信息在内的对象。

例如,您可以使用GraphicsGradientFill 类 创建一个具有渐变填充属性的对象。请注意,GraphicsGradientFill类的属性与您在基本绘图API中用作 Graphics.beginGradientFill()函数的参数十分相似。同时也要注意的是,其它执行IGraphicsData的类也相当于基本 绘图API中的一些现有函数。在ActionScript 3.0 Developer’s Guide中的Using graphics data classes 中,您可以找到一个关于新数据类和封装的Graphics 类方法的完整说明表格。

  • 3.使用绘图数据的新图形类函数: Graphics 类有了新的方法来解释从向量对象和IGraphicsData类型对象中得来的数据,并把这些数据用于图形的描绘。这些新函数包括 Graphics.drawPath(), Graphics.drawGraphicsData()和 Graphics.drawTriangles()。

使用绘图数据

此例中,我们将设置一些绘图数据并将其传递到Graphics.drawPath() 函数中,以此来介绍如何在新的绘图API中使用绘图数据。Graphics.drawPath()函数具有三个参数(前两个参数是向量对象):


drawPath( commands: Vector.< int> , data : Vector.< Number> ,  winding: String = "evenOdd" ) : void


命令向量对象是一系列用于定义每段绘图路径的绘图命令的GraphicsPathCommand 值。 正如我之前所言,GraphicsPathCommand将一组线条绘制命令抽象成为一组常量。所以,Graphics.moveTo()变成了 1,Graphics.lineTo()变成了2。现在您就可以将一系列代表绘图命令的常量填充到一个向量对象中,同时还能使用此向量作为 Graphics.drawPath()的参数来描绘一条图形轨迹。

特别地,请看看先前“基本绘图命令”部分中的例子,它有一个moveTo()命令,其后跟随有几个lineTo()命令,这几个命令中的每一个都指 向一个坐标系 。每一个命令都被简单抽象成为一个数字,这意味着您现在可以在一个向量对象(例如1,2,2,2)存储一系列命令,而不必一次只能执行一个命令了。然后您 可以将向量对象作为Graphics.drawPath()的命令参数进行传递。

数据向量对象是一系列坐标点,它们对应于命令参数中用于绘制轨迹的一个个绘图命令常量。每一对参数确定了一个x/y坐标对,所以,两个数值就是一个 坐标点,四个数值就是两个坐标点,以此类推。数据向量对象中每两个条目与命令向量对象中一个条目相匹配,从而生成一条通向某个坐标点的轨迹(或动作)。

winding值决定了是否填充以及如何填充交叉部分的图形。对于绘制像正方形那样的简单图形,winding就不是那么重要了。然而当您开始使用 交叠线条绘制较为复杂的图形时,winding值决定了是填充整个图形,还是仅填充其中一部分。如果想了解更多信息,请参阅ActionScript 3.0 Developer's Guide中的Defining winding rules。

还是以跟前面“基本绘图命令”部分中使用Graphics.drawPath()绘图的同一个正方形为例。(现在,我们继续使用基本API绘制线条 和填充类型,这样我们可以重点关注用于绘图命令和坐标的数据的使用)。此例首先使用Vector.push()函数将一系列绘图命令填入 square_commands向量对象。然后将一系列坐标对填入square_coord向量对象。最后,Graphics.drawPath() 函数使这两组数据匹配并作为参数来描述这个正方形。

示例


      //定义线条类型
      graphics.lineStyle(2,0x000000); 
      //定义填充
      graphics.beginFill(0x666699);
      //设置颜色
      //为命令参数建立新的向量对象
      var square_coord:Vector. = new Vector.();
      //使用向量数组push()函数添加moveTo()和lineTo()值。
      // 一个 moveTo命令,后跟三个lineTo命令
      square_commands.push(1, 2, 2 ,2, 2);
      // 为数据参数建立一个新的向量对象
      var square_coord:Vector. = new Vector.();
      //使用向量数组push()函数添加一组坐标对
      square_coord.push(10,10, 10,100, 100,100, 100,10, 10,10);
      graphics.drawPath(square_commands, square_coord);


对于如同简单正方形这样的图形绘制,新的API似乎并未为我们节省了多少工作。 然而,对于一个较为复杂的图形,你可以发现新的API简化了代码编写:

示例


      //定义线条类型。
      graphics.lineStyle(2,0x000000);
      //定义填充色
      graphics.beginFill(0x666699) ;
      //设置颜色 
      //为数据参数建立新的向量对象
      var star_commands:Vector. = new Vector. ();
      //使用向量数组push()函数添加moveTo()和lineTo()值
      //一个moveTo()命令,后面跟随三个lineTo()命令
      star_commands.push(1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2);
      //为数据参数建立一个新的向量对象
      var star_coord:Vector. = new Vector.();
      //使用向量数组push()函数添加一组坐标对
      star_coord.push(0,0, 75,50, 100,0, 125,50, 200,0, 150,75, 200,100, 150,125, 200,200, 125,150, 100,200, 75,150, 0,200, 50,125, 0,100, 50,75, 0,0);
      graphics.drawPath(star_commands, star_coord);


结果

此星型图案使用了16条lineTo()命令,它们都被抽象成了一行独立的数值,并使用Vector.push()函数添加到了 star_commands向量中。在Graphics.drawPath()加载两个向量对象的同时,您可以改变向量对象的属性值,还可以用新的属性值 重新绘图,或者让某个函数将属性值传递到向量对象,然后重新绘图,这样您或者其他用户都可以在程序运行期间随意改变图形。在下一部分中,我们将介绍一个添 加用户交互的例子,但是首先我们也应该了解怎样使用向量对象设置笔划和填充属性。

使用绘图类型数据

在“使用绘图数据”部分,我们为Graphics.drawPath()函数载入了可作为绘图数据的向量对象。现在,我们将定义一些绘图类型对象用 作Graphics.drawGraphicsData()函数的参数。Graphics.drawGraphicsData()函数在运行新绘图API 方面的功能十分强大。它使用了运行flash.display.IGraphicsData界面的类所创建的对象。这些类具体如下:

  • GraphicsBitmapFill
  • GraphicsEndFill
  • GraphicsGradientFill
  • GraphicsPath
  • GraphicsShaderFill
  • GraphicsSolidFill
  • GraphicsStroke
  • GraphicsTrianglePath

每一个类都能够创建定义填充类型、笔划或相应轨迹的属性对象。它们都可以将那些对象作为参数传递到 Graphics.drawGraphicsData(),从而按照已定义的属性进行图形描绘。一旦您定义了绘图对象,就可以将它们重复应用到其他图形 中,因此您的绘图属性就可以集中起来,而且可以转移到其他图形。

第一个例子仅在IGraphicsData对象中置入了填充和轨迹数据。 对于绘图数据,该例子使用了如下的类:

  • 1.用于填充属性的GraphicsSolidFill类
  • 2.用于图形轮廓的GraphicsPath类

示例


      //创建填充属性
      var myFill:GraphicsSolidFill = new GraphicsSolidFill();
      myFill.color = 0x33CCFF;
      //创建轨迹属性
      var myPath:GraphicsPath = new GraphicsPath(new Vector.(), new Vector.());
      myPath.commands.push(1,2,2,2,2);
      myPath.data.push(10,10, 10,100, 100,100, 100,10, 10,10);
      //置入IGraphicsData向量数组
      var myDrawing:Vector. = new Vector.();
      myDrawing.push(myFill, myPath);
      //图形描绘
      graphics.drawGraphicsData(myDrawing);


结果

现在,让我们使用GraphicsStroke类来定义一个笔划。请记住,我们需要向IGraphicsData对象添加新的 GraphicsStroke对象,以使Graphics.drawGraphicsData() 得到笔划属性。请注意,笔划也需要一个填充值来确定线条类型。对于绘图数据,该例子使用了以下的类:

  • 用于笔划属性的GraphicsStroke类和GraphicsSolidFill 类(GraphicsStroke类其实也可以使用GraphicsGradientFill或GraphicsShaderFill类来确定线条外形。)
  • GraphicsPath 类

示例


      //创建图形填充属性
      var myFill:GraphicsSolidFill = new GraphicsSolidFill();
      myFill.color = 0x33CCFF;
      //创建笔划及其填充属性
      myStroke:GraphicsStroke = new GraphicsStroke(2);
      myStroke.fill = new GraphicsSolidFill(0x000000);
      //创建轨迹属性
      var myPath:GraphicsPath = new GraphicsPath(new Vector.(), new Vector.());
      myPath.commands.push(1,2,2,2,2);
      myPath.data.push(10,10, 10,100, 100,100, 100,10, 10,10);
      //置入IGraphicsData 向量数组
      var myDrawing:Vector. = new Vector.();
      myDrawing.push(myFill, myStroke, myPath); 
      //图形描绘
      graphics.drawGraphicsData(myDrawing);


结果

下面是一个稍复杂一些的使用GraphicsGradientFill类的填充范例。GraphicsGradientFill类和其属性很大程度 上反映了Graphics.beginGradientFill() 函数的参数。实际上,许多图形数据类和其属性都在图形类函数中有对等的结构内容。此例子也使用了 flash.geom.Matrix.createGradientBox()函数用于对 GraphicsGradientFill.matrix 属性的设置。

示例


      // 创建填充属性
      var myFill:GraphicsGradientFill = new GraphicsGradientFill();
      myFill.colors = [0xEEFFEE, 0x0000FF];
      myFill.matrix = new Matrix();
      myFill.matrix.createGradientBox(100, 100, 0);
      // 创建笔划属性
      var myStroke:GraphicsStroke = new GraphicsStroke(2);
      myStroke.fill = new GraphicsSolidFill(0x000000);
      // 创建轨迹属性
      var myPath:GraphicsPath = new GraphicsPath(new Vector.(), new Vector.());
      myPath.commands.push(1,2,2,2,2);
      myPath.data.push(10,10, 10,100, 100,100, 100,10, 10,10);
      // 置入 IGraphicsData向量数组
      var myDrawing:Vector. = new Vector.();
      myDrawing.push(myFill, myStroke, myPath);
      // 图形描绘
      graphics.drawGraphicsData(myDrawing);


结果

重用绘图数据以及添加用户交互

到目前为止,我们已经完成了以下工作:

  • 1.建立图形数据对象来定义笔划、填充和轨迹属性。
  • 2.创建向量对象作为参数,传递到绘图命令
  • 3.使用Graphics.drawPath() 和 Graphics.drawGraphicsData() 函数绘制图形。

我们来将图形数据对象与一个比正方形稍复杂一些的图形联系起来,同时在运行时创建一个函数来改变图形。

示例


      // 创建一个帮助控制的显示对象
      // 显示列表中的图形
      var mySprite:Sprite = new Sprite();
      mySprite.x = 10;
      mySprite.y = 10;
      addChild(mySprite);
      //创建绘图数据
      // 渐变填充对象
      var myFill:GraphicsGradientFill = new GraphicsGradientFill();
      myFill.colors = [0xEEFFEE, 0x0000FF];
      myFill.matrix = new Matrix();
      myFill.matrix.createGradientBox(300, 300, 0);
      // 轨迹对象
      var myPath:GraphicsPath = new GraphicsPath(new Vector.(), new Vector.());
      myPath.commands.push(1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2);
      myPath.data.push(0,0, 75,50, 100,0, 125,50, 200,0, 150,75, 200,100, 150,125, 200,200, 125,150, 100,200, 75,150, 0,200, 50,125, 0,100, 50,75, 0,0);
      // 组合对象从而完成绘制
      var myDrawing:Vector. = new Vector.();
      myDrawing.push(myFill, myPath);
      // 图形描绘
      mySprite.graphics.drawGraphicsData(myDrawing);


结果

现在让我们来添加一些用户交互。在下面的例子中,随着用户在图形上拖动鼠标,星形图的底角点也会跟随光标移动。星形图会时而叠起时而展开。为了顺利 完成这个例子,我们在平台上添加了一个鼠标事件检测器。在鼠标事件控制器(即redraw()函数)上,清除掉旧的角坐标点,再用 Vector.splice()函数添加由鼠标位置确定的数值。同时也加入了if语句,使得图形能够在用户拖动边角一定距离后迅速跳回到它原来的位置。

请注意: redraw()事件操作函数在改变图形坐标和描绘新图形之前会使用Graphics.clear()函数删除当前的图形。否则,您的新图形就会覆盖在其它图形之上。



      // 创建绘图数据
      // 渐变的填充对象
      var myFill:GraphicsGradientFill = new GraphicsGradientFill();
      myFill.colors = [0xEEFFEE, 0x0000FF];
      myFill.matrix = new Matrix();
      myFill.matrix.createGradientBox(300, 300, 0);
      // 轨迹对象
      var myPath:GraphicsPath = new GraphicsPath(new Vector.(), new Vector.());
      myPath.commands.push(1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2);
      myPath.data.push(0,0, 75,50, 100,0, 125,50, 200,0, 150,75, 200,100, 150,125, 200,200, 125,150, 100,200, 75,150, 0,200, 50,125, 0,100, 50,75, 0,0);
      // 组合对象从而完成绘制
      var myDrawing:Vector. = new Vector.();
      myDrawing.push(myFill, myPath);
      //图形描绘
      graphics.drawGraphicsData(myDrawing);
      stage.addEventListener(MouseEvent.MOUSE_MOVE, redraw);
      function redraw(event:MouseEvent):void {
      // 使用 clear() 命令删除旧坐标的图形
      graphics.clear();
      var x:Number = event.stageX;
      var y:Number = event.stageY;
      myPath.data.splice(16, 2, x, y);
      graphics.drawGraphicsData(myDrawing);
      // 如果拖拽距离太远,就迅速跳回到原来的图形
      if(x > 350 || y > 300) {
      graphics.clear();
      myPath.data.splice(16, 2, 200, 200);
      graphics.drawGraphicsData(myDrawing);
      }
      }


结果

请在图形上拖动鼠标

我想您现在已经领略到了新的绘图API的强大的功能了吧。 还有更多的复杂函数可以用于置入绘图坐标,有些甚至可以改变GraphicsPath命令数据。 您可以使用函数持续地置入一些图形数据对象,来继续生成和改变图形。 或者,您可以使用颜色主题和其他需要的设置来建立一些绘图数据,同时您始终都可以将同一个数据对象重用到整个项目或不同项目的其它图形中。

对定位,互动性和列表显示的考虑

下面的例子表明了,当您结合ActionScript绘图功能在Flash Professional的设计平台上进行绘图时,您需要注意显示列表的显示顺序问题。 在使用ActionScript绘图时,图形命令在Sprite上运行,所以您可以对图形位置、顺序和互动性进行更多的控制。 Shape类和Graphics类都不会继承InteractiveObject类,但是Sprite类可以继承。 如果您使用Shape类或Graphics类在根显示对象上绘制一个对象,意料之外,您或许不能够响应鼠标点击或键盘事件。 而如果在一个Sprite类的实例上绘图,您就可以使用InteractiveObject类的所有事件和属性了。

在Flash Professional中,图形背景的坐标网格可以使用Line工具在平台上进行绘制。在mySprite对象中创建的图形会排列成一行,并与显示列表(即在前台)中的mySprite坐标和位置相互对应。

示例(可以在bars.fla示例文件中找到)


      //创建一个帮助控制的显示对象
      //显示列表中的图形
      var mySprite:Sprite = new Sprite();
      mySprite.x = 40; mySprite.y = 50; addChild(mySprite);
      // 创建绘图数据 
      // 笔划对象
      var myStroke:GraphicsStroke = new GraphicsStroke(2);
      myStroke.joints = JointStyle.MITER;
      myStroke.fill = new GraphicsSolidFill(0x102020);
      // 实心笔划
      //填充对象
      var myFill:GraphicsGradientFill = new GraphicsGradientFill();
      myFill.colors = [0xEEFFEE, 0x0000FF];
      myFill.matrix = new Matrix();
      myFill.matrix.createGradientBox(300, 300, 0);
      // 轨迹对象
      var myPath:GraphicsPath = new GraphicsPath(new Vector.(), new Vector.());
      myPath.commands.push(1,2,2,2,2);
      myPath.data.push(0,0, 240,0, 240,60, 0,60, 0,0);
      // 组合对象从而完成绘制
      var myDrawing:Vector. = new Vector.();
      myDrawing.push(myStroke, myFill, myPath);
      // 图形描绘
      mySprite.graphics.drawGraphicsData(myDrawing);
      //创建一个文本输入域
      var myTextField:TextField = new TextField();
      myTextField.type = TextFieldType.INPUT;
      myTextField.width = 180;
      myTextField.height = 20;
      myTextField.x = 20;
      myTextField.y = 80;
      myTextField.background = true;
      myTextField.backgroundColor = 0xCAE1FF;
      myTextField.border = true;
      myTextField.text = "Type a number here and press Enter";
      myTextField.restrict = "0-9";
      mySprite.addChild(myTextField);
      // 在用户点击文本域时添加一个接收器
      myTextField.addEventListener(MouseEvent.CLICK, fieldClickHandler);
      // 添加一个键盘按键接收器
      myTextField.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
      // 在用户点击并输入数值时清空文本域
      function fieldClickHandler(event:MouseEvent):void {
      myTextField.text = "";
      }
      //如果按键为Enter键
      // 改变drawGraphicsData() 函数的数据数组值
      function keyDownHandler(event:KeyboardEvent):void {
      if (event.keyCode == Keyboard.ENTER) {
      mySprite.graphics.clear();
      var x:String = myTextField.text;
      myPath.data.splice(2, 3, x, 0, x);
      mySprite.graphics.drawGraphicsData(myDrawing);
      }
      }


结果

测试示例文件

存档的示例文件压缩包中包括了以下文件

  • starflake.fl :Flash Professional CS4文件,用于第一个用户交互中可以拖动底角的星形图示例。
  • starflake_CS5.fla :Flash Professional CS5版本的文件,用于第一个用户交互中可以拖动底角的星形图示例。
  • bars.fla :Flash Professional CS4文件,用于用户交互中可以设置宽度的条形图示例。
  • bars_CS5.fla :Flash Professional CS5文件,用于用户交互中可以设置宽度的条形图示例。

测试示例

  • 1.在本网页的顶部下载as3_graphics_commands.zip文件。

理解代码

ActionScript的示例代码可以在每个FLA示例文件中找到:

  • 1.随着FLA示例文件在Flash Professional中打开,选择主Timeline(时间轴)的第一帧。
  • 2.选择Window(窗口)>Actions(功能)的菜单选项(或按住F9键),从而打开Actions面板,即可看到ActionScript。
阅读更多
个人分类: Actionscript3
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭