原文链接:http://www.insideria.com/2008/05/an-introduction-to-degrafa-1.html
One of the most important aspects of delivering a rich user experience is what a user sees and interacts with on the screen. Graphics, whether static or dynamic, comprise the face of an application. Static graphics can definitely suffice for creating the necessary visual parts of an application, but to change them you must replace them with other static graphics. Dynamic graphics, created with programmatic drawing are very powerful because it can be manipulated at run-time, but it can involve more advanced knowledge of the Drawing API. Degrafa is a declarative graphics framework that aims to offer the best of both worlds.
In this article you will be introduced to some of the theory behind Degrafa, the basics of composition, advanced implementations, and how the framework may fit into your Flex or AIR projects. There are many uses for Degrafa, all of which can’t be covered in this article, but the hope is that this information may reveal something useful for you.
Degrafa is an open-source project created by developers in the RIA community. One of the primary goals is to allow designers and developers to forego the complexities of ActionScript to create dynamic graphics in MXML markup. With Degrafa you can define fills, strokes, shapes, complex paths, and more in the same way you would define a Flex component in MXML. Degrafa can be used to create skins, data visualizations, maps, or meet any other graphics need.
Some of the features of Degrafa include:
• A library of pre-composed shapes for you to use as well as the ability to create your own.
• Use SVG path data to create complete reusable shapes.
• Flexible options for fills and strokes.
• Binding of properties for Fills, Strokes and Geometry.
• Advanced CSS support for layered backgrounds, complex border control and background image positioning based on CSS 3.
• Productive features like derivatives, composition reuse, shape libraries, repeaters, and other utilities that allow you to do more with less code.
• Light weight model-based architecture.
• Compatibility with Flex 2 and Flex 3.
• A "pay-as-you-go" implementation.
• Much, much more...
Terminology
In the following examples there will be terminology used that you may or may not be familiar with. Some terms revolve around graphics, Flex, or the tags used in Degrafa. Before you get started, here's some definitions of some of those terms you'll see used in the remainder of this article:
geometry - A general term referring to any shape, path or graphic.
graphics context - A drawing destination where vector drawing commands can occur and stores information on how commands get rendered like line styles or fill styles. Any objects derived from Shape, Sprite or Movie Clip have a graphics property. Anything visual is drawn to or stored within a graphics context.
graphicsTarget - A property available to Degrafa geometry, like Circle, RegularRectangle, Polygon, etc. It allows you to specify the target(s) as a destination for Degrafa to render to. Acceptable targets must have a graphics context.
Surface - A tag in Degrafa provided as a base UIComponent for composing Degrafa elements.
GeometryGroup - A tag is used within the Surface tag and allows you to group Degrafa objects together. GeometryGroup is based on Sprite, so you can take advantage of the properties and events supported by Sprite.
GeometryComposition - A Degrafa tag that works similarly to GeometryGroup, but it allows you to compose or group elements at any level within your MXML file, including the root. You can optionally render those graphics to any object containing a graphics property using the graphicsTarget property.
Fill - A fill is a graphics term for the treatment of the inner area of an object. Fills are applied to the shapes you create. Degrafa has a variety of fills you can take advantage of like Solid, Linear Gradient, Radial Gradient, Bitmap, Blend and Complex fills.
Stroke - A stroke is a graphics term for the treatment of the edge of an object, usually defined with a thickness. Degrafa provides Solid, Linear Gradient and Radial Gradients strokes.
Getting Started with Degrafa
To use Degrafa in your projects you have two options; you can either download the latest version of Degrafa as a compiled SWC, or tap in directly to the Degrafa SVN. Both of these options are hosted openly on
1. Go to Google Code where the Degrafa code is hosted and download the latest version of the Degrafa SWC for the version of Flex you’re running. There is a version for Flex 2 and Flex 3. After you’ve downloaded the Degrafa SWC, open up Flex Builder and create a new project called DegrafaExample.
2. Once you’ve created your new Flex project, you need to drop the Degrafa SWC you just downloaded into the libs folder.
3. For the sake of keeping things clean in your MXML you can add a Degrafa XML namespace in the root MXML tag where you’ll be using Degrafa. For example, adding the Degrafa namespace in the Application tag of DegrafaExample.mxml could look like this:
Google Code. For the purposes of this article you can use the compiled Degrafa SWC by following these steps:
1. Go to Google Code where the Degrafa code is hosted and download the latest version of the Degrafa SWC for the version of Flex you’re running. There is a version for Flex 2 and Flex 3. After you’ve downloaded the Degrafa SWC, open up Flex Builder and create a new project called DegrafaExample.
2. Once you’ve created your new Flex project, you need to drop the Degrafa SWC you just downloaded into the libs folder.
3. For the sake of keeping things clean in your MXML you can add a Degrafa XML namespace in the root MXML tag where you’ll be using Degrafa. For example, adding the Degrafa namespace in the Application tag of DegrafaExample.mxml could look like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:Degrafa="http://www.degrafa.com/2007"
layout="absolute">
</mx:Application>
The Basics of Composition By following those steps you can now access any of the Degrafa MXML tags for creating your graphics. Before you do though, there are a few fundamentals you’ll want to keep in mind when composing graphics. The following steps expose some of these fundamentals and walks through the basics of creating graphics with Degrafa resulting in a final composition of a single white Circle shown in Figure 1 and viewable here.
Figure 1 - Basic Circle
1. Graphic objects defined with Degrafa are drawn by a series of commands that require an object to be rendered upon. To define this you can either use a Degrafa GeometryGroup tag within a Surface tag, like this:
<Degrafa:Surface>
<Degrafa:GeometryGroup>
<!-- Add Degrafa Shapes Here -->
</Degrafa:GeometryGroup>
</Degrafa:Surface>
The Degrafa Surface tag is a UIComponent, which means you can set properties like verticalCenter, horizontalCenter, etc. and the GeometryGroup tag is a Sprite, which has fewer properties than UIComponent.
2. The other option you have for specifying an area to draw Degrafa graphics to is to use the graphicsTarget property with a GeometryComposition or other geometry tag like Circle, RegularRectangle, etc. This will allow you to define graphic objects outside of the component and target any graphics context, i.e., any component that accepts graphics, like a Canvas, Button, Image, etc. This will be the method used in this basic example.
3. Add a Canvas in the application MXML and give it an id of myCanvas. Also, add a GeometryComposition and specify myCanvas in the graphicsTarget property like this:
<Degrafa:GeometryComposition
graphicsTarget="{[ myCanvas ]}">
<!-- Add Degrafa Shapes Here -->
</Degrafa:GeometryComposition>
<mx:Canvas
id="myCanvas"/>
4. Once you’ve specified the graphics context you want to draw to you can start drawing Degrafa geometry. There are a number of pre-created Degrafa geometry objects that you can use to create your compositions, like Circle, RegularRectangle, Path, LineRepeater, and more. These objects can be drawn within a GeometryGroup, GeometryComposition, or by themselves using the graphicsTarget property. For this example, add a Circle within the GeometryComposition:
<Degrafa:GeometryComposition
graphicsTarget="{[ myCanvas ]}">
<Degrafa:Circle
radius="100"
centerX="120"
centerY="120"/>
</Degrafa:GeometryComposition>
<mx:Canvas
id="myCanvas"/>
5. That last bit of MXML draws a Circle, but you can’t see it because it needs to have a fill or stroke applied to it. Degrafa comes with a number of fill and stroke capabilities that are sure to meet the majority of your needs. There are Solid, Linear Gradient, Radial Gradient, Blend, and Complex Fills. Stroke types include Solid, Linear Gradient, and Radial Gradient options. To get the Circle in this example to show up you can apply a SolidFill within the Circle like this:
<Degrafa:GeometryComposition
graphicsTarget="{[ myCanvas ]}">
<Degrafa:Circle
radius="100"
centerX="120"
centerY="120" >
<Degrafa:fill>
<Degrafa:SolidFill
color="#FFF"/>
</Degrafa:fill>
</Degrafa:Circle>
</Degrafa:GeometryComposition>
<mx:Canvas
id="myCanvas"/>
Note: With the binding features of Degrafa you can also specify Fills and Strokes outside of geometry and assign the fill or stroke by referencing its id. This is a great way to repurpose fills and strokes across geometry objects. The following MXML snippet shows the same fill used above, but pulled outside of the Circle tag and assigned using the fill’s id of myFill:
<Degrafa:fills>
<Degrafa:SolidFill
id="myFill"
color="#FFF"/>
</Degrafa:fill>
<Degrafa:Circle
radius="100"
centerX="120"
centerY="120"
fill="{myFill}"/>
View the final product or view the source.
Thoughts on Binding
One of the features I use the most in Degrafa is binding. Binding means creating a relationship between two properties such that when one updates the other is dynamically updated. This can be a very powerful feature when you think about binding properties like width, height, radius, fill, stroke, etc. between geometry objects to create some very dynamic relationships for a variety of purposes.
A simple example would be binding the angle of a LinearGradientFill to a HorizontalSlider so that the angle of the gradient would change dynamically based on the updated value of the slider (don’t forget to set live dragging on the slider). A more complex example would be a list of items whose backgrounds change based on dynamic data.
When you start to think of all the data you could use to influence graphics drawn with Degrafa it seems the possibilities are endless; interfaces that change based on cursor coordinates, buttons that change at different stages in a process, icons that change based on time of day, and charts that change shape based on new data. All it takes is a little experimentation with your own data sets, various public APIs, and user input. With binding in Degrafa you’ll start to see opportunities to create dynamic graphics in Flex.
A More Advanced Implementation
Now that we’ve covered some of the capabilities of Degrafa and the basics of composing graphics, let’s take a look at more involved example. The following steps will walk through creating a Flex Clock (Figure 2) to illustrate some of the features of Degrafa mentioned throughout this article and create a more advanced graphics composition. You can see the final product here.
Figure 1 - Flex Clock
1. To get started, create a new Flex application within your DegrafaExample folder and name it FlexClock.mxml. For convenience, add the same XML namespace mentioned in the previous example to your Application tag:
xmlns:Degrafa="http://www.degrafa.com/2007"
2. For this example, you’ll be composing the different parts of the Flex Clock as GeometryGroups to take advantage of some of the properties of Sprite, like rotation for the clock hands.
The first thing you need to do is set up the Surface tag so you can start adding the parts of the clock as GeometryGroups. Set the x and y of the Surface to 100 to position the clock within your application.
3. Add a GeometryGroup tag within the Surface tag and give it an id of clockFace. Within this GeometryGroup you’ll create the face of the clock, which is made up of the dark gradient background and letters “Fx”.
To create the dark grey face of the clock you can use a RegularRectangle object inside the clockFace GeometryGroup. Set the height and width of the RegularRectangle to 360. You also need to create that dark grey radial gradient fill for the RegularRectangle, but at this point the clockFace MXML should look like this:
<Degrafa:GeometryGroup
id="clockFace">
<Degrafa:RegularRectangle
width="300"
height="300"/>
</Degrafa:GeometryGroup>
4. Outside the clockFace GeometryGroup, create a new fills tag. Within that fills tag, add a RadialGradientFill tag and set the cx property to 40, cy to 20, radius to 440 and give it an id of greyRadFill. The last thing you need to do to finish this fill is assign gradient stops within the fill.
Gradient stops are the attributes that make up a gradient and have their own set of properties, including color, alpha, and ratio. In Degrafa, you use a GradientStop tag to create a gradient stop within a radial or linear gradient fill. You can set as many gradient stops as you need to get the desired effect you’re looking for. For the purposes of this example, just create two gradient stops. Give the first gradient stop a color of #444 and the second a color of #000.
Note: You may have noticed that the colors for the gradient stops were defined using three digits rather than the six that Flex normally requires. This is similar to what is doable in HTML and another feature of Degrafa. In fact, you have several options for specifying colors within the color property. You can specify a color with a HEX value (#666), RGB value (60,60,60), color key (grey), or CMYK (0,0,0,60).
5. Now that you’ve created the dark grey fill, it must be applied to the RegularRectangle you created earlier. As mentioned previously in this article, you can apply a fill to an object using the fill property. The MXML for the RegularRectangle and RadialGradientFill should look like this:
<Degrafa:fills>
<Degrafa:RadialGradientFill
id="greyRadFill"
cx="40"
cy="20"
radius="340">
<Degrafa:GradientStop
color="#444"/>
<Degrafa:GradientStop
color="#000"/>
</Degrafa:RadialGradientFill>
</Degrafa:fills>
<Degrafa:GeometryGroup
id="clockFace">
<Degrafa:RegularRectangle
width="300"
height="300"
fill="{ greyRadFill }"/>
</Degrafa:GeometryGroup>
6. If you run your application you should see the RegularRectangle you created filled with a dark grey radial gradient. There’s the base for the clock face.
Now you can create the remainder of the fills and strokes up front for the other elements of the clock and apply them as you go. You need to create a solid white fill for the “Fx” letters, a 10 pixel solid red stroke for the hour and minute hands, and a 2 pixel light grey stroke for the second hand. The fills tag has already been specified, but you need to add a strokes tag as well. Adding these new fills and strokes to the greyGradFill you already created should look like this in your MXML:
<Degrafa:fills>
<Degrafa:RadialGradientFill
id="greyRadFill"
cx="40"
cy="20"
radius="340">
<Degrafa:GradientStop
color="#444"/>
<Degrafa:GradientStop
color="#000"/>
</Degrafa:RadialGradientFill>
<Degrafa:SolidFill
id="whiteFill"
color="#FFF"/>
</Degrafa:fills>
<Degrafa:strokes>
<Degrafa:SolidStroke
id="redStroke"
color="#C50"
weight="10"
caps="none"/>
<Degrafa:SolidStroke
id="greyStroke"
color="#666"
weight="2"
caps="none"/>
</Degrafa:strokes>
Note: You could expose Fill and Stroke properties to a designer to allow for customization without having them get involved with code.
7. With all the fills and strokes you’ll need for this example created, let’s add the remaining parts of the clock. You already have the dark grey background created, so now you need to finish the clock face and add the “Fx” letters. For these letters you will use a Polygon tag within the clockFace GeometryGroup.
For the “F” create a Polygon tag within the clockFace and assign whiteFill to the fill property. A Polygon takes shape by specifying a series of points, so rather than completely ending the Polygon tag, close it and set a points tag within. To specify the individual points, use a GraphicPoint tag for each point and specify the x and y values for each point. Instead of making you figure out the points to define the “F” I’ve defined them in the MXML block below along with the MXML for the “x”:
<!-- The White "F" Polygon -->
<Degrafa:Polygon
x="74"
y="90"
fill="{ whiteFill }">
<Degrafa:points>
<Degrafa:GraphicPoint
x="1.716"
y=".868"/>
<Degrafa:GraphicPoint
x="91.638"
y=".868"/>
<Degrafa:GraphicPoint
x="92.329"
y="21.965"/>
<Degrafa:GraphicPoint
x="30.768"
y="21.965"/>
<Degrafa:GraphicPoint
x="30.768"
y="71.768"/>
<Degrafa:GraphicPoint
x="85.758"
y="71.768"/>
<Degrafa:GraphicPoint
x="85.758"
y="95.632"/>
<Degrafa:GraphicPoint
x="30.768"
y="95.632"/>
<Degrafa:GraphicPoint
x="30.768"
y="164.11"/>
<Degrafa:GraphicPoint
x="1.37"
y="164.11"/>
</Degrafa:points>
</Degrafa:Polygon>
<!-- The White "x" Polygon -->
<Degrafa:Polygon
x="178"
y="140"
fill="{ whiteFill }">
<Degrafa:points>
<Degrafa:GraphicPoint
x="1.107"
y=".147"/>
<Degrafa:GraphicPoint
x="32.926"
y=".147"/>
<Degrafa:GraphicPoint
x="54.022"
y="39.574"/>
<Degrafa:GraphicPoint
x="76.503"
y=".147"/>
<Degrafa:GraphicPoint
x="105.209"
y=".147"/>
<Degrafa:GraphicPoint
x="72.354"
y="54.446"/>
<Degrafa:GraphicPoint
x="108.322"
y="113.932"/>
<Degrafa:GraphicPoint
x="73.736"
y="113.932"/>
<Degrafa:GraphicPoint
x="52.256"
y="73.43"/>
<Degrafa:GraphicPoint
x="32.505"
y="113.241"/>
<Degrafa:GraphicPoint
x=".07"
y="113.241"/>
<Degrafa:GraphicPoint