Please Wait: An Image-Loading Spinner

By Josh Marinacci, September 24, 2008

This sample shows you how to build an animated spinner that is displayed while loading an image over the network, and then fades away once the image is completely loaded.

Loading an image from Flickr.com

Understanding the Code

The core of this sample is the ImageLoadingSpinner class. It is a custom node which draws a spinner over a particular image. The class monitors the image's progress variable. When the progress reaches 100 the spinner fades away, showing the image underneath. This process is performed by the internal progress variable, as shown in Figure 1.

Source Code
public class ImageLoadingSpinner extends CustomNode {
    public var image:Image;
    public var width:Integer = 50;
    public var height:Integer = 50;
    
    var spinnerAnim:Timeline;
    var fadeAnim:Timeline;
    var progress = bind image.progress on replace {
        if(progress >= 100) {
            fadeAnim.start();
        }
    }

Figure 1: Monitoring the Image's Progress

The rest of this class creates the actual spinner. It is a group of four circles that rotate around a center. This is done with the code in Figure 2, which loops from 0 to 3, creating a translucent circle offset from the center and rotating by the angle variable plus an offset. When the angle variable is animated the circles are animated as well.

Source Code
    override public function create():Node {
        var angle = 0;
        var spinner = Group {
            content: [
                // grey background
                Rectangle { width: bind width height: bind height fill: Color.DARKGRAY },
                //spinning circles
                Group {
                    translateX: bind width/2
                    translateY: bind height/2
                    content: for(n in [0..3]) {
                        Circle { fill: Color.rgb(255,255,255,0.5) 
                            centerX: 30 centerY: 30 radius: 50 rotate: bind angle+n*90
                        }
                    }
                }
            ]
        };
        
        spinnerAnim = Timeline {
            repeatCount: Timeline.INDEFINITE
            keyFrames: [
                at(0s) { angle => 0 tween Interpolator.LINEAR },
                at(2s) { angle => 360 tween Interpolator.LINEAR }
            ]
        };
        spinnerAnim.start();

Figure 2: Spinner Code

The actual returned node in the create function is a group that contains the image with the spinner on top of it. The fade animation lowers the opacity of the spinner over half a second and stops the animation so that CPU cycles are not wasted.

Source Code
fadeAnim = Timeline {
    keyFrames: [
        at(0s) { spinner.opacity => 1.0 tween Interpolator.LINEAR },
        KeyFrame { 
            time: 0.5s values: spinner.opacity => 0.0 tween Interpolator.LINEAR
            action: function():Void { spinnerAnim.stop(); }
            },
        ]
};

return Group {
    content: [ ImageView { image: image }, spinner ]
}

Figure 3: Fade Animation

Looking Ahead

This example could be extended by creating other spinners with different animation or loading multiple images with a spinner in between them in an animated slideshow.