Spinning Preloader – An All-Time Classic in Pure AS3

Update (December 11th, 2008): The source code is available for download now. The zip archive also includes a FlashDevelop project file. Get it here.

This tutorial may serve as a beginner exercise in AS3 coding. But before starting, let’s take a quick look at what we will be creating:

The spinning preloader animation is an all-time classic. Our implementation will be highly configurable. You will be able to set the number, size, color and fade-out duration of the rectangular segments, as well as the radius and the spinning speed.

Prerequisites

Since this is a pure AS3 project, you can choose whatever IDE you like. I use the open-source AS3 IDE FlashDevelop along with the free Flex 3 SDK. The only other thing we need is the AS3 animation kit TweenLite. This lightweight tweening engine is contained in a single class file. You can eiher copy and paste it into your project space, or you can store it somewhere else and add the path to your project’s classpaths settings.

I call the preloader class SpinningPreloader and use com.log2e.utils as package definition. So my FlashDevelop project panel looks like this:

Spinning Preloader Project Panel

The SpinningPreloader Class

Here’s the full class code (I will explain it below):

  1. package com.log2e.utils
  2. {
  3.    import flash.display.DisplayObjectContainer;
  4.    import flash.display.Sprite;
  5.    import flash.events.TimerEvent;
  6.    import flash.utils.Timer;
  7.    import gs.TweenLite;
  8.  
  9.    public class SpinningPreloader extends Sprite
  10.    {
  11.       private var _target :D isplayObjectContainer;
  12.       private var _container:Sprite;
  13.       private var _centerX:uint;
  14.       private var _centerY:uint;
  15.       private var _radius:uint;
  16.       private var _steps:uint;
  17.       private var _rectWidth:uint;
  18.       private var _rectHeight:uint;
  19.       private var _color:uint;
  20.       private var _count:uint;
  21.       private var _timer:Timer;
  22.       private var _timerInterval:uint;
  23.       private var _fadeOutDuration:Number;
  24.  
  25.       public function SpinningPreloader( target :D isplayObjectContainer,
  26.                                          centerX:uint = 0,
  27.                                          centerY:uint = 0,
  28.                                          radius:uint = 15,
  29.                                          steps:uint = 24,
  30.                                          rectWidth:uint = 4,
  31.                                          rectHeight:uint = 2,
  32.                                          color:uint = 0x0000FF,
  33.                                          timerInterval:uint = 20,
  34.                                          fadeOutDuration:Number = 1.5 )
  35.       {
  36.          _target = target;
  37.          _centerX = centerX;
  38.          _centerY = centerY;
  39.          _radius = radius;
  40.          _steps = steps;
  41.          _rectWidth = rectWidth;
  42.          _rectHeight = rectHeight;
  43.          _color = color;
  44.          _timerInterval = timerInterval;
  45.          _fadeOutDuration = fadeOutDuration;
  46.       }
  47.  
  48.       public function start():void
  49.       {
  50.          _container = new Sprite();
  51.          _container.x = _centerX;
  52.          _container.y = _centerY;
  53.          _target.addChild(_container);
  54.  
  55.          _count = 0;
  56.          _timer = new Timer(_timerInterval, 0);
  57.          _timer.addEventListener(TimerEvent.TIMER, drawRectangle);
  58.          _timer.start();
  59.       }
  60.  
  61.       public function stop():void
  62.       {
  63.          if (_container)
  64.          {
  65.             TweenLite.to( _container, .5, { alpha:0, onComplete:onContainerFadeOutComplete } );
  66.          }
  67.       }
  68.  
  69.       private function onContainerFadeOutComplete():void
  70.       {
  71.          _timer.stop();
  72.          _target.removeChild(_container);
  73.       }
  74.  
  75.       private function onRectFadeOutComplete(rect:Sprite):void
  76.       {
  77.          _container.removeChild(rect);
  78.       }
  79.  
  80.       private function drawRectangle(e:TimerEvent):void
  81.       {
  82.          var rot:Number = _count * 360 / _steps;
  83.          var rect:Sprite = filledRectangle(_rectWidth, _rectHeight, _color);
  84.          rect.x = _radius * Math.cos(rot * Math.PI/180);
  85.          rect.y = _radius * Math.sin(rot * Math.PI/180);
  86.          rect.rotation = rot;
  87.          _container.addChild(rect);
  88.          _count++;
  89.  
  90.          TweenLite.to( rect, _fadeOutDuration, { alpha:0, onComplete:onRectFadeOutComplete, onCompleteParams:[rect] } );
  91.          e.updateAfterEvent();
  92.  
  93.          if (_count == _steps)
  94.          {
  95.             _count = 0;
  96.          }
  97.       }
  98.  
  99.       private function filledRectangle(width:uint, height:uint, color:uint):Sprite
  100.       {
  101.          var rect:Sprite = new Sprite();
  102.          rect.graphics.beginFill(color);
  103.          rect.graphics.drawRect(-width/2, -height/2, width, height);
  104.          rect.graphics.endFill();
  105.          return rect;
  106.       }
  107.  
  108.    }
  109. }

The Constructor

The constructor receives a list of parameters:

  • target: The display object you want to attach the preloader animation to. The type is DisplayObjectContainer so the target can be any object that inherits from this class (for example Stage, MovieClip, Sprite).
  • centerX: The x position of the preloader’s center point.
  • centerY: The y position of the preloader’s center point.
  • radius: The radius of the preloader. Please note that this is not the outer radius of the resulting animation but the distance between the center point and the registration point of the rectangular segments.
  • steps: The number of rectangles that are created in one full turn.
  • rectWidth: The width of a single rectangular segment.
  • rectHeight: The height of a single rectangular segment.
  • color: The color of the rectangles.
  • timerInterval (in ms): This parameter controls the speed of the rotation by setting the time interval after which a new rectangle is drawn.
  • fadeOutInterval (in s): This parameter specifies the duration of the alpha tween that fades out it each segment.

The start() and stop() Methods

These two methods are the only interface between the SpinningPreloader class and the outside world.

The start() method creates a container for the animation and positions it on the target display object. It also initializes the _count property and starts the timer which repeatedly calls the drawRectangle() method.

The stop() method fades out the container sprite. Once the alpha value is 0 the timer is stopped and the container is removed from the display list. If you prefer to remove the animation immediately just replace the line of code that starts the tween with the two lines from the onContainerFadeOutComplete() event handler method.

The “Fading” Event Handlers

The method onContainerFadeOutComplete() is called after the container’s alpha value has reached 0.

The method onRectFadeOutComplete() is called whenever a single rectangle has finished its life cycle. As soon as the rectangle’s alpha value is 0 it is removed from the display list. This way we limit the maximum number of rectangles that are child objects of the container sprite at the same time.

Drawing the Rectangles

The angle that defines the position of each rectangle is calculated with the help of a counter variable (_count) which is incremented each time a rectangle is drawn. If the counter value is equal to the number of steps _count is reset to zero. The result from 360 divided by the number of steps muliplied with _count gives us the angle in degrees. This value is stored in the local variable rot.

In order to calculate the exact x and y coordinates we need a little trigonometry.

Trigonometry

(Just on a side note, we use the three o’clock position as starting point because that’s where the 0 degree angle is situated in Flash.) The sine and cosine in the above diagram are calculated by sin(A) = y/r and cos(A) = x/r. This trigonometric relation is used in the drawRectangle() method:

rect.x = _radius * Math.cos(rot * Math.PI/180);
rect.y = _radius * Math.sin(rot * Math.PI/180);

Please note that the Math.sin() and Math.cos() functions expect the angle to be in radians, that’s why we have to multiply rot with Math.PI/180.

The rectangles themselves are created in the filledRectangle() method which receives the width, height and color as parameters. The sprite instance is returned to drawRectangle() where the x, y and rotation values are set and the rectangle is added to the display list. As soon as the rectangle is visible it starts to fade out. Invoking e.updateAfterEvent() causes the Flash Player to update the screen immediately after a rectangle has been added (instead of relying on the next scheduled screen update that is governed by the frame rate).

Using the SpinningPreloader Class

The animations shown on this page were created by instantiating three preloader objects with different parameters:

  1. package
  2. {
  3.    import flash.display.Sprite;
  4.    import com.log2e.utils.SpinningPreloader;
  5.  
  6.    public class Main extends Sprite
  7.    {
  8.       public function Main():void
  9.       {
  10.          var pl1:SpinningPreloader = new SpinningPreloader(this, 50, 70, 15, 24, 4, 2, 0x0000FF, 20, 1.5);
  11.          pl1.start();
  12.  
  13.          var pl2:SpinningPreloader = new SpinningPreloader(this, 180, 70, 40, 48, 8, 3, 0xFF0000, 30, 2);
  14.          pl2.start();
  15.  
  16.          var pl3:SpinningPreloader = new SpinningPreloader(this, 330, 70, 30, 32, 12, 4, 0×999999, 15, 1);
  17.          pl3.start();
  18.       }
  19.    }
  20. }

Room for Improvement

Where to go from here? There are several options for improving the class. For example, you could add a text field at the center of the preloader and an update function that receives the loading progress value. Or you could use a circle or a small bitmap instead of the rectangle. I will leave this as an exercise to you.

If you have any questions or suggestions leave a comment or drop me a message.

Tags: , , ,

28 Responses to “Spinning Preloader – An All-Time Classic in Pure AS3”

  1. juan says:

    This is the most usefull tutorial I’ve ever read. I’m a newby in flash, and the DisplayObjectContainer was like finding gold for my intent of making a reusable preloader. Thanks so much.

  2. Steve Good says:

    I am trying out your tutorial but I’m getting an error.

    Line 32: 1084: Syntax error: expecting rightparen before 0000.

    Any ideas?

  3. Stefan Schmalhaus says:

    @Steve: Well, it’s just a syntax error, so take a closer look at your code again. It’s probably a missing closing brace or an omitted parenthesis.

  4. Michael says:

    I too am trying to figure out what bracket or parenthesis is missing….

  5. Michael says:

    I’m having a hard time getting this to work. Is there any way you could post the .fla for download?

    I’m also having difficulty dealing with the package call.

  6. Uenuku says:

    There isn’t a bracket or parenthesis missing, the webpage changed a lowercase x to alt+0215 “×” in the color code.

  7. Michael says:

    Uenuku,

    That was It! Thank you

  8. Matt says:

    How do you go about integrating this with loading an image or something so that all the lines are shown? The way I have it right now it skips most of them since the image loads fast and doesn’t have time to count each percent number.

  9. Gary says:

    I’d really like to add this to my library but I’m not getting the final step for implementation. I guess I’m below “beginner.” Without a sample .fla, I’ll just have to store this away for another day.

  10. Matt says:

    I appreciate this tutorial but really need to agree with the others who mention the final step is puzzling. I’m completely new to AS3 and OOP, and while I appreciate a good challenge, it would be cool to have some closure on this awesome class!

  11. Andrei says:

    Thanks man, this is superb!

  12. Ivo says:

    Is there a reason why SpinningPreloader extends Sprite ? It doesnt seem like itself gets added to the display list.

  13. Stefan Schmalhaus says:

    @Ivo: Good eye! You are right, the class SpinningPreloader doesn’t necessarily have to extend Sprite. I guess I had originally planned to make it a display object itself. The _target property makes this obsolete though. Thanks for pointing this out.

  14. LopLawswott says:

    Very usefull post.
    Thanks.
    P.S. I like your writing style.

  15. First of all congratulation for such a great site. I learned a lot reading article here today. I will make sure i visit this site once a day so i can learn more.

  16. Jill says:

    That is really impressive, But unless there is a .fla file, it’s difficult to get what your doing.

    Could you submit the .fla file and or have a link to the file it’s easier to understand files when pulling them apart.

  17. @Jill: You can easily create the .fla file yourself: Create a new file in Flash CS3/CS4 and assign the “Main” class from the ZIP archive (http://blog.log2e.com/demos/spinning-preloader/SpinningPreloader.zip) as document class.

  18. Rob says:

    Would this be very hard without using the TweenLite library ? I’m trying to adapt the code and use the tween class but appearently this is harder than i thought…

  19. Rob says:

    Ok I’ve managed to get it (partly) working. Changed the object to a circle and replaced the tweenlite functions with AS3 Tweens.

    A problem appeared though, when I added a TweenEvent.MOTION_FINISH for the onFadeOutComplete function. After a while the animation seems to freeze or stop working, the objects stop fading out.

    It looks likes this is a garbage collector problem, but using an array for the tweens that i reset when it reaches the _count variable, and removing the tween event listeners don’t fix this problem. Anyone got an idea how to fix this ?

  20. Prunnybrula says:

    Hi all!

    My name is Steven and Im new around here :) . So far this is an excellent place for information and I’ve spent quite a bit of time reading and browsing around. Look forward to hearing from you!

  21. sideDoor says:

    Man, thanks so much for this! Awesome and lite!

    sd

  22. Diana says:

    This is awesome !!! Good work

  23. sicminded says:

    @Rob

    Use tweenlite, that’ll fix the problem.

  24. dutch says:

    Can anyone help me pls?
    I am doing some papervision animation….i created my main class and evything works perfectly….now i want a preloader,but i do not know how to implement preloaders on .as files,better said how to make it wait until whole animation is loaded and then to start it…i am using flash cs3,and i hav created .fla file which uses only one .as file as docment class….
    If anyone understood what i mean…pls…this is killing me

  25. Natasha says:

    Very nice.

    Your code is flawed, but it would be very easy to fix it. Your event listener is never removed. Therefore, your timer will never be garbage collected and neither will your preloader.

    A memory leak!

  26. vsj says:

    Thanks for the awsome post, i like the way u describe the session.
    i was looking for one for some time.

  27. Long time viewer / first time poster. Really enjoy reading the blog, keep up the excellent work. Will definitely start posting more in the future.

  28. I know this is well documented and you are a very nice human beeing sharing it with a big community and you don’t need to do that.
    But I’m a helpless little piece of crap in AS3 and I need to know how to implement the loader class: After hours of trying it would be better for me if you were less well-intentioned.

Leave a Reply