Advertisement
  1. Code
  2. Designing
Code

Drawing Beautiful Components With Code - Made Easy

by
Difficulty:IntermediateLength:MediumLanguages:

Most programmers have never touched design. If you have always wanted to beautiful graphics in your Flash projects but never knew how to use Adobe Photoshop or Illustrator, this tutorial is for you. Nice graphics can be created entirely with code. Drawing graphics for components with code even has a few advantages.

Prerequisites

This tutorial requires that you know how to perform basic tweens using Greensock's TweenLite or TweenMax. You can download the latest version of TweenLite from the Greensock website. I will be using Flash CS5 Professional to complete this project. You may use any Flash IDE you'd like (such as FlashDevelop or FDT). It would also help to have a fair understanding of inheritance.

A Brief Introduction

Before we get started, let's take a look at what we'll be learning and why it's important. When designing Flash applications there are many ways to generate graphics. You can use an external tool such as Adobe Illustrator or draw complex graphics with Adobe Photoshop. You can even use 3D utilities such as Cinema 4D to create eye-popping 3D bitmaps to import into Flash. These techniques are great for creating a well designed static application - static meaning not very dynamic, or in most cases, not dynamic at all.

If you use one of the above methods to generate graphics for Flash, you may find it a little difficult to resize the graphics without some loss of quality or, even worse, distortion. On the sunny side, you can import a vector file that was generated with Adobe Illustrator into your Flash application and scale the image infinitely without distorting the image - but when you want to change the overall ratio of the image, you will notice that your image becomes a little deformed.

What we are going to do today is learn how efficient it can be to draw our application's graphics with code. We are going to create a beautiful animated button that is completely generated with ActionScript.

Step 1: Pros and Cons

Before we dig in, I'd like to discuss the importance of planning for your application and answering simple questions to decide whether to draw the graphics with code or by hand (e.g. with Adobe Photoshop). To help us answer the questions below, let's first examine the pros and cons to drawing with code.

The Pros

  • Portability: Graphics that are written in code are easy to import into other projects since they exist as AS files. No need to copy and paste an image into a new Flash project or import the image into the library. Just import the appropriate classes.
  • Simplicity: Graphics drawn with code are done using vectors so they have a delicate and simplistic feel that is both nice to look at and light on the CPU.
  • Consistency: When you'd like to resize your graphics, they will not distort (unless this is the effect you are going for) and they will maintain quality as the are scaled. Bitmap images, for example, become pixelated as they are made larger or zoomed into. Vector images don't lose any quality or become pixelated. You can zoom into a vector image infinately and it will never lose quality. You can also be sure that your graphics still look amazing on screens with higher resolutions. As technology advances and new screen resolutions become available, your graphics are more likely to still be able to deliver a nice quality, as opposed to bitmap images that may not look so good.
  • Dynamics: Along with being able to resize and scale your graphics, you can also re-color segments of an image or component with very few lines of code. Changing properties of a graphic has never been so easy. What's that you say? You no longer want the frame of your textfield to have rounded edges? No problem here. I'll just change one line of code... and we're good to go!
  • Precision: Although many graphics applications have features that simplify the positioning of graphical segments, you'll find it easier to make modifications and position the contents of a graphic with code. You can easily center one segment inside another as the parent segment changes size or write an algorithm for handling how a segment will be displayed as it is resized. Hint, hint.

The Cons

  • Detail: Vector graphics drawn with code can be very detailed, but too much detail can have a dramatic effect on the performance of our applications. Although it may be possible, you won't find me attempting to draw a detailed Spongebob Squarepants with code. Something like that should be done using Adobe Illustrator. Also vectors may not be able to be as detailed as a complex graphic drawn in Adobe Photoshop. Vectors tend to contain a smaller file size than your typical bitmap image. Vectors are connected points and bitmaps are represented pixel by pixel. But if you converted a bitmap into a vector image, the new vector's file size would turn out to be a lot larger than the original bitmap image. This said, you won't be able to paint an intricate portrait with code. Use Adobe Photoshop instead.
  • Limitations: There may be times where you'd like to use your graphics in different media. Sorry to say it but if you won't be able to drop your dynamic graphics into that Java game you've been working on or that Objective-C application you just started. There are ways to render images using Flash but you will definately lose any dynamic capabilities provided by code.
  • Difficulty: Not to say that you will find it ridiculously difficult to draw graphics with code but depending on what you are drawing, you will most likely find the entire process a bit more complicated than using a software that is intended for this purpose. Don't expect drawing with code to be an easy task. It really all depends on the expected outcome.

You should now find the following questions pretty easy to answer. There are three questions you need to ask yourself before you start your project:

  1. Does your application require graphics to be static or dynamic?

    1. Static
    2. Dynamic
  2. Do the graphics or components in your application need to be exclusive to Flash or be consistent across multiple platforms?

    1. Flash Platform only
    2. Multiple Platforms
  3. Is the graphic design of your application more detailed or quite simple?

    1. Relatively Simple (Basic Geometric Shapes)
    2. Moderate (Creative Designs with Simple Shapes)
    3. Intricate (Overly Complex Masterpieces)

Now examine your test results. If your test results closely resemble the results below, than the techniques we are getting ready to learn are for you?

  1. #2
  2. #1
  3. #1 or #2

If your results do not look like the results above, you may want to consider other methods. But for the rest of us, let's proceed.

Step 2: Brainstorming

So here's the scenario. A client has just asked us to create an animated button for their Flash site and AIR app. The button must be consistent with the science fiction theme of the client's website. The button's height can be a fixed size but the button must be able to accomodate for different widths. The button must have a toggle state that has a blatant difference between it's active and inactive states. Finally the button must have an API that's easy to work with.

Now that we know that we've got this themed component to create, we need to think about how we are going to go about creating it. Well, we know that we're going to be drawing a lot of graphics with code so that the component will be easily and efficiently editable. Why not start off with a base class that will add high level functionality on top Flash's drawing API?

We don't want to draw graphics over and over again by writing the same lines of code so we'll have a class with particular properties and methods specifically designed for reusing the code for us. We'll extend this class to create the main shapes that we'll need for our button.

Step 3: The Editable Shape Class

Now it's time to get to it! Create a new class called EditableShape and add the following classes to the classpath.

Now create the class declaration. The class should extend the flash.display.Sprite class.

Create the following variables(properties) before the class constructor.

Within the class constructor add the following code and I'll explain what we just did.

The primary function of the constructor is to instantiate all of the properties that we have defined. We also gave each property a default value. You will be able to see what the result of these values are later. Each property that begins with an underscore will be given read/write access to outside code. The reasons these properties are not public properites is because we want to respond to the change in value of each particular property. We shall do this within the setter methods that we'll create later.

Each property is based on particular value that must be passed into any of the graphics object methods. One example is the _fillGradientColors property. It is an array of uints that will be passed into the graphics.beginGradientFill method. Another example is the _pixelHinting property. It will be passed into the graphics.lineStyle method's pixelHinting parameter.

The last line of the constuctor calls the init method. The init method is very simple. It calls the update method.

Step 4: The Update Method

The update method is probably the most important method within the EditableShape class. The main objective of this method is to clear any existing graphics and redraw them based upon the latest data. We will call this method everytime a property has changed so the changes can be seen immediately by the user if necassary. Create the update method.

The Matrix objects we created in the constructor are used to manipulate gradients. We needed two of them. One for the fill gradient and the other for the line gradient.

We will be overriding the get graphics method so that we can forbid access to this property to outside code. We don't want any outside code tampering with our graphics at all. Because we are going to override the get graphics method, we will need to access the method from the subclass. This is accomplished through the super object. We will also create a protected method called getGraphics later on. This method is needed to give subclasses access to the graphics object without granting access to the object to outside code.

In conclusion, the update method re-initializes the graphics and it does this based on the current values of the properties within the EditableShape instance. The last line of code calls the draw method which is an abstract method in the EditableShape class. The method should be overriden by a subclass and is intended for drawing a particular shape using the graphics object within its implementation.

Step 5: Clones and Copies

Create the draw method.

Again the draw method is just a simple abstract method that we can ignore for now.

We will need to copy the properties of one shape onto another. There may also be times when you'd like a complete clone of a particular shape. We'll create two methods to accomplish this for us. Doing so will save us a lot of time in the long run. Instead of always re-writing code we'll just call the appropriate method which copies or clones a shape. Create the copy and clone methods.

The copy method takes the shape parameter and sets all of the properties equal to that of the current EditableShape.

We will not be using the clone method in this tutorial but I just threw it in as an extra bonus. The method returns a cloned copy of the current EditableShape class.

Step 6: Finishing Up the EditableShape

The last thing we need to do is grant read/write access to all of the shape's main properties. Write the following getter and setter methods within the EditableShape class.

All of the getter methods just return the corresponding properties value. But for the setter methods we correct or filter out any unwanted input. We also call the update method so that there is an immediate reaction to the new value or values.

Step 7: Drawing Rectangles

The majority of our button will consist of rectangle shapes. Unless we want to bore our client's users with a bunch of lame blocky shapes, we will want to be able to round the edges of our rectangles. The EditableShape class is meant to be extended, meaning that it should have subclasses that finalize the primary function of the class. The EditableShape class takes care of all of the hard work for us. All we have to do now is draw the shape within a subclass. Create a new class called RectangleShape which extends the EditableShape class. The class declaration should look as follows.

Add the following private properties to the class directly after the class declaration.

I mentioned before that we will be rounding the edges to our rectangles. As you have probably guessed already, we will be granting read/write access to these properties just as we did with all of the other inherited properties. This will allow us to react to any change the ellipseWidth and ellipseHeight properties immediately after they have been altered.

Reminder: This is accomplished by calling the update method.

Create the class constructor. Give the _ellipseWidth and _ellipseHeight properties a default value of 0. We don't want the edges to be rounded by default.

You should remember the draw method from the EditableShape class was an abstract method. That is a method that has an empty implementation. We are going to override this method to give it the proper functionality. The methods name is self explanatory. The method will do the actual drawing of the shape.

Important: We call the base class's constructor, super(), after we have assigned a value to the _ellipseWidth and _ellipseHeight properties. Remember the the constructor initializes the shape by calling the init method which then calls the update method which finally calls the draw method. You will see in a minute that the draw method requires these properties. Forgeting to call the constructor last will result in an argument error thrown by the drawRoundRect method of the graphics object.

I have to apologize to all of you who were expecting ten or more lines of code here in the draw method. That's not the case here. Remember that the base class(EditableShape) does most of the work for us. It handles colors, gradients, alphas, line properties and so on. All we needed to do is draw the shape and that's what we've done. The graphics object can no longer be accessed by outside code since the property has been overriden so we had to access it through the protected method, getGraphics. Finally we call the drawRoundRect method on the graphics object and we pass in the appropriate parameters.

Step 8: Overriding the Copy and Clone Methods

There are two more methods we need to override before we can conclude this class. The first method is the copy method. This is the method from the EditableShape class that takes an EditableShape as a parameter and causes the properties of the parent shape onto the parameter shape. We need add some functionality to this method. We will be overriding this method but that doesn't mean that we have to forget everything that the base class does with this method. We'll use the super object to call the method from the base class so that it inherits its original functionality while adding new functionality to it.

Can you tell what just happened? If the parameter, shape, is a RectangleShape we also copy the ellipseWidth and ellipseHeight properties. If you have a collection of RectangleShapes, that you are using to draw a graphic with, and you'd like them all to have the same ellipseWidth and ellipseHeight properties, this will keep you from writing too many extra lines of code.

The second method we need to override is the clone method.

The clone method also inherits the implementation of the base class. But with this implementation we have added the ellipseWidth and ellipseHeight properties.

Step 9: More Access

Write the following getter and setter methods:

We have not only granted the read/write access that is needed by outside code, but we have also made sure that the ellipseWidth and ellipseHeight are never below zero. There is also an immediate reaction to these properties being set or changed.

Note: If you do not want to correct any values for the ellipseWidth or ellispeHeight properties, you do not have to. There may be a time when you'd want the ellipseWidth or ellipseHeight to be less than zero.

Now that we have our first shape complete. Let's take a quick look at what our shape looks like when we first create it. You will need to create a document class and create a new instance of the RectangleShape object within it. Add the new RectangleShape to the stage. Here's my code:

Let's look at the result.

This is the what our shape looks like by default. Feel free to play around with it a bit so that you get a good feel for it. Try changing the colors or modifying the size.

Step 9: Drawing Circles

We are going to create a sort of bubbling effect for the over state of our button. This means that we will need bubbles. Create the EclipseShape class. Make sure that it extends the EditableShape class.

Once again the power of inheritance has allowed us to recycle old code instead of writing the same code over and over again. This class only contains inherited properties and methods. We had to override the draw method in order to create the actual eclipse shape though. The drawEclipse method uses the inherited width and height properties to draw our shape. And once again we access the graphics object using the protected getGraphics method.

We now have all of the shapes we need to draw our button.

Step 10: Starting the Button

Create a new class that extends flash.display.Sprite. You can call the class whatever you'd like. I'm going to use the name MyButton. Import the following classes.

Define the following properties.

Just like before, any property that starts with an underscore is either expected to cause an immediate reaction when set or the property will be read-only.

The _width property will be used to override the width property of the base class and we redraw the shape everytime the width property is set.

The active property is going to be used to swap the toggle states of the button later.

You will notice several properties that are RectangleShapes, several properties that are TextFields, and a Sprite. We will see the role each of these properties play later.

We'll be storing all of our bubbles in the circles Array. To improve performance we will need to pause various tweens. In order to do this we will need to store these tweens in an array so that they can be accessed whenever we need them.

Last we have the circleBlur property which is a BlurFilter that is to be applied to each bubble, and we have the _toggle property which affects the behavior of the button.

Create the following public constants.

The height of our button will not change so it's appropriate to store the value of the button's height in a class constant. The size of the button's ellipse is relative to the button's height. If the button becomes too large or too small, our button may lose the Pill Button look that we are going for. Also the number of bubbles is predetermined so we store this value in a class constant as well.

Step 11: The Base

When ever I am drawing graphics for a component like a text field or a button, I always, always, always... start with a base shape. This is a shape that acts as the foundation of the shape. It is the backbone of the component. How the graphic is structured is entirely dependent on how I construct the base.

You will notice a property that we have defined called toggleBase. I am not talking about this property. I am talking about the base property itself. This shape will define the outline of the button. Let's create this property and initialize its values now. Add the following code to the constructor of the class that you have defined as your button.

The default fillGradientRotation is 0. This value is in degrees but is converted into radians when passed into the gradient box(Matrix). Along with initializing the button's size, we have to set the rotation of the gradient. In this case we want this value to be 90 degrees. We also set the fillColors (as an Array), and the lineColors.

It is important to remember that the default value for these properties is an Array that has a length of two. All of the Array properties(lineColor,lineAlpha, and lineRatios) must be the same length. If they aren't, the graphics will not draw. This is why we set the lineAlphas and lineRatios properties.

If you test the Flash movie, you should get this.

This is the overrall shape we are going for, "The Gel Pill". Let's make it shine a little bit.

Step 12: The Reflection

We've got the main structure of the button down already but now we need to add the small thing that I love to preach about. We need lots and lots of depth. We can start by adding the reflection. When light hits a glossy surface, it reflects off of the surface. Instantiate and initialize the ref (short for reflection) property.

We have turned the alpha property down a little bit so the the reflection isn't so intense but we have basically followed the same steps to draw the ref shape as we did with the base shape. And of course we have sized and positioned the reflection relative to the size of the button's height. Let's see what we have now.

Not too bad. Still needs a lot more depth though. We'll add some more later.

Step 13: The Inner Light

Sometimes when you are creating art, you notice that it's those tiny little details that make the biggest difference in the appearance of your work. The light property will add the tiny detail into our button that makes all of the difference. This property will also be used a little differently in our button's over and out states, but for now it will make our button appear to have a semi-transparent surface.

Add the following code to your button's constructor method.

The constructor should now look like this:

We have added a GlowFilter and a BlurFilter to this object so that we can achieve a specific look. We don't want the shape to look like a shape at all but empty space within the button. Notice that we have used the copy method to mimic the properties of the ref object. There's no need to write the same lines of code for the light object. Just mimic the ref property and make just a few modifications and you have yourself a new shape.

See how we are beginning to see more depth as our shape is being constructed? The pill doesn't look fully opaque and it isn't very transparent either. We want an "Out of This World" kind of look so just in between is perfect.

Step 14: Simple Text

Now that we have a good foundation down on the table, it's appropriate to begin adding the first TextField to the button. Initialize the txt property. And create a mask that is the same size and ellipse as the base so that the text doesn't appear outside of the button.

We have once again utilized the copy method by creating a copy of the base object. We could have very well have used the clone method also. The result would be the same. Also note that we have set the filters array of the txtMask object to a blank array. This is because when the copy method used the filters property is also inherited. We have just simply overriden this property. Let's see where we're at.

We will position all of the TextFields later on. For now our txt will just have to hang out on the left side of our button.

Step 15: Setting Up for the Over State

Our button will illuminate and display a sort of science fiction like animation sequence with bubbles when the mouse hovers over the button. Of course the entire sequence will be accomplished 100% with code.

But before we can do this we need to create the elements of the sequence. The first is the background object, or the glow property, of the over state. Let's add the glow object now. Add the following code to the constructor method:

We will also need a container for the bubbles to be displayed in and a mask for that container so that the bubbles don't float out of the button.

Step 16: Bubbles

Now we are going to blow a few bubbles. Add the following lines of code to the constructor method:

The circleBlur is a BlurFilter that will be applied to all bubbles. This will give each bubble the specific science like look that we're going for. We also need to create the arrays that will store all of the bubbles and the tweens that will animate each bubble.

We need a method that will generate all of the bubbles we need.

Use a loop to iterate a block of code that creates a new EllipseShape object, or in this case a bubble, and run the resetCircle method on the bubble. We haven't created this method yet but it re-initializes the parameter bubble. In the loop, the method simply initializes each bubble since they haven't been initialized to begin with. Finally the stopEffect method is called. This method pauses the bubble's animation sequence. We'll write this method in a bit.

For now, create the following methods. (Also now is a good time to import com.greensock.TweenMax).

The randomNumber method returns a random number based on the parameters passed into the method. The first parameter specifies a the minimum value that can be generated and the second parameter specifies the maximum value that can be generated. We need this method to generate a random location and size for a bubble when we reset a bubble.

This brings me to the resetCircle method. I said before that this method re-initializes the specified bubble. To be more specific, the method recycles a bubble and makes it appear like a brand new bubble that has just been created when in reality it is just another old bubble.

The to method from the TweenMax class returns the tween that the bubble contains. We push this bubble into the tweens array. The bubble starts below the button and rises above the button. When the tween is finished it is no longer needed. The tween needs to be removed (using the removeTween method) and the bubble needs to be reset or recycled. The anonymous function doComplete does just that.

Before we can see what our bubbles look like we need to create two methods that will control playback of the bubbles' animation sequence. Besides if we added a call to the createEffect method in the constructor and tested our movie now, the bubbles would be all there but we wouldn't see them because they would be beneath the button. Remember that each bubble is added to the effectContainer in the resetCircle method and that the effectContainer is masked so that the bubbles aren't seen outside of the button.

(Plus, if you tested the movie now you'd just get a compile-time error for not implementing the stopEffect method that is called when we create our bubbles.)

Loop through the tweens array to play each tween and to pause each tween when needed. Very simple.

Now add a call to the createEffect method and the playEffect method within the constructor. Test your movie. You should now have a beautiful display of bubbles. When you are finished remove the playEffect method from the constructor that you just added but keep the createEffect method.

Step 17: Glowing Text

Let's add the glowing TextField to the button. We're using a seperate TextField, effectTxt instead of txt, so that we can do a fading transition into the next state. We need an additional TextField for this. We also need another mask for our TextField just as we did with the first one.

Just another TextField with a glow filter. Again we will position the text fields later when we create the update method. Now you should have something that looks like this.

Now that all of the elements for the over state have been generated, we only have one more task to complete. Next we position the contents of the button.

Step 18: Positioning

Encapsulated in our button class is a function that positions the button's contents based on the current value of the _width property. The method I am referring to is the update method. Call the update method on the last line of code of the constructor method. Then create the update method.

Resize particalar objects and reposition others. Test the movie:

We now have centered text. Set the alpha property of all of the effect base objects(effectContainer, effectTxt and glow) to zero. Re-test the movie and now you should see the original state of the button.

Step 19: The Over State

At last we finally get to the ever-so-exciting "over" state. Okay let's go! Add the following lines of code at the end of the constructor method.

Now create the overState and outState event handler methods.

The overState method plays the bubbles animation and performs a fade in transition of the effect whenever the mouse hovers over the button.

The outState method does the complete opposite: it returns the button back to its original state when the mouse hovers off of the button, lets the light property linger for a second to give the button a sense of losing power or losing illumination.

Comment out the upState method so that you can test the movie without any errors. We'll create this method soon.

Important: Remember to uncomment out the call to the upState method within the outState method after we have created that method.

Step 20: Up and Down States

Add the following code at the end of the constructor method.

Create the following methods.

The downState method displays a background glow around the button when the mouse button is down and the upState button removes that glow return the button back to it's initial state. The upState method's e parameter is set to null by default. This allows the method to be called manually and not require the dispatching of a MouseEvent. We'll test to see where were at after the next step.

Reminder: Uncomment the call to the upState method from the outState method.

Step 21: Toggle Button

Add the missing code to the constructor method.

We have just created the all of the objects that we need for what I call the active state. When the button's _toggle property is set to true, the button behaves differently. We'll see how in a moment. First add the following lines of code to the update method.

Now create two new methods. The goActive method and the goInactive method.

Let's take a quick look at each method. The goActive method animates to the active state only if the button is inactive. It then set's the _active property to true. The goInactive method does the complete opposite. If the button is active, it returns to an inactive state and sets the _active property to false.

Now for the final step in creating toggle behavior for our button. Add this code to the upState method.

The code above checks to see if the method has been called do to an event being dispatched (we don't want this block of code to execute otherwise) and it checks to see if the button's toggle property has been enabled. Then the button travels to the appropriate state based on its current state. In other words, it toggles.

Set the _toggle property to true somewhere in the constructor and test the movie.

We now have a beautiful button. There's just one last problem. Our button isn't editable - well, not from outside of itself anyway. So, next were going to create the class's getters and setters.

Step 23: Edit This

First let's override the width property to give access to our custom _width property.

Don't allow the value to be lower than zero. Set the _width property and call the update method.

Now override the height property.

Trying to set the height property just throws an error because this property now has a fixed value which is the BUTTON_HEIGHT constant.

Create the following getter and setter methods.

When the label is set we immediatly react to the change by setting the text values of all of the TextField objects and we position them by calling the update method. The active property changes states based on the new value. When we set the toggle property, if it's new value is false we check to see if the button is active and if it is we switch it to its original state.

Trying setting the width property to 300 and the label to "Push Me", and making the button an active toggle button.

Step 24: More Depth

I said earlier that we would add more depth to the button. I also said that the tiny details make a large difference. Well this can be accomplished very quickly and easily by applying a couple of filters. Add these two lines of code anywhere in the constructor method. Then test the movie.

Let's see what we have now. The button on top is the original and the button on the button is the new and improved button with the above filters.

Wow! Look at that button pop out. Now you can see how important it is to use filters when drawing graphics to add in shadows and other little details that make your graphics that much better.

Extras

There are a couple of features within the EditableShape class that we didn't have time to use. One feature is the clone method. It returns a clone of the parent shape. Another is the useBitmapFill property. You must specify a valid BitmapData object by setting the bitmapData property and you have to set useBitmapFill to true.

Here's our button with a bitmap fill applied to the base property.

I also didn't discuss the matchGradientSize property which is true by default. If set to false the width and height of the fill and line gradients will not mimic the size of the actual shape. You can get create by deactivating this property. You can even use TweenLite (or TweenMax) to animate these and most of the properties in the EditableShape class.

Conclusion

Well our button is complete and our client is happy. We have only scratched the surface. There's a lot more that you can do with Flash's drawing API. I challenge you to make a CustomShape class that extends EditableShape. The class should use the lineTo methods of the graphics object to draw irregular shapes. Have fun with it. Thanks for tuning in. We've learned a lot. See you next time.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.