Build a Slider Microsite with GreenSock's Timeline Lite
During the this tutorial I'm going to take you through building a simple slider that will scroll a personal microsite. We'll use the GreenSock Timeline Lite class and demonstrate just how straight-forward it can make your Flash animation workflow.
Final Result Preview
Let's take a look at the final result we will be working towards:
Introduction
From time to time, changes happen in our lives, changes that force us re-think the way we act. This is especially true in our community, where we are faced daily with changes that question the way we build what we build.
Some of these changes are for the worst, like loosing .php webservice support in Flash ide, but most of them are for the better, like optimizing tips. Once in a while someone makes a class that revolutionizes the way we think about Flash.
Like Papervision3d was for 3d, or box2d for physics, there is now Timeline for animation. This simple package will ultimately change the way you structure ActionScript animation, making it possible to create an infinite amount of virtual timelines, completely dynamically, giving you full control of your animation. If it doesn't, you can just add whatever feature you need as a plugin.
Step 1: Where to Get it
This is the most difficult part of the whole tutorial..



Just go to http://blog.greensock.com/timelinemax and download a copy of the GreenSock tweening platform for AS3. It's a zip file, save it to your hard drive, export all of it to a folder and copy the "com" folder to the root of where you plan to use the class. The whole lot is very well documented (as you can see in the docs folder) and you even have an ease visualizer.
Step 2: Files Needed
Besides the com folder I created a Main.as to serve as a document class and a TimelineMicrosite.fla for UI drawing. I also copied badge-made-with-tweenmax-108x54.gif from the badges folder that came in the zip file we downloaded earlier.



Step 3: Structure
I'm not going to focus on how to create a user interface, as this is completely up to you. I will, however, give you the guidelines I followed to make this microsite.
Begin by creating five layers and name them: background, slides, navigation, player and footer.

Step 4: Background and Slides
The background layer contains a simple gradient, nothing else. The slides layer contains several movieclips. Each movieclip is an area of the microsite. In this case they are home_mc, about_mc, works_mc and contacts_mc. Each one of them has nested movieclips with instance names.



Step 5: Navigation, Player and Footer
The navigation layer has a navigation_mc movieclip, inside of which there is a selection_mc. Its structure is as shown in the image below.

The footer is just an import of the tweenmax badge. Here's the full tree:

Step 6: Document Class
You all know how this is done right? In case you've forgotten here's the skeleton for a Main document class:
1 |
|
2 |
package { |
3 |
|
4 |
public class Main extends MovieClip{ |
5 |
|
6 |
public function Main():void { |
7 |
|
8 |
}
|
9 |
}
|
10 |
}
|
Step 7: Importing
If you are using fdt, flash builder, eclipse with flex sdk or flash develop, you probably import these automatically, but if you are using the Flash ide to code, then besides my sympathy you will also need to import:
1 |
|
2 |
package { |
3 |
|
4 |
import flash.display.MovieClip; |
5 |
import flash.events.MouseEvent; |
6 |
import flash.geom.Rectangle; |
7 |
import com.greensock.TweenLite; |
8 |
import com.greensock.TimelineMax; |
9 |
import com.greensock.events.TweenEvent; |
10 |
import com.greensock.easing.Linear; |
11 |
|
12 |
public class Main extends MovieClip{ |
13 |
|
14 |
public function Main():void { |
15 |
|
16 |
}
|
17 |
}
|
18 |
}
|
Step 8: Main Function
You want your main class function to setup the scene for you.
1 |
|
2 |
public function Main():void { |
3 |
//This will setup the animation of the slides
|
4 |
setupSlides(); |
5 |
|
6 |
//This will setup the slides navigation
|
7 |
setupNavigation(); |
8 |
}
|
Step 9: Slides Variables
We will be working with these variables:
1 |
|
2 |
//An array that will store the slides for easier access
|
3 |
private var slides:Array; |
4 |
|
5 |
//A Number that will set a margin for the slides
|
6 |
private var offset:Number |
7 |
|
8 |
//A timeline that will store the slide tweens
|
9 |
private var slideline:TimelineMax |
10 |
|
11 |
//A timeline that will store each slide tween
|
12 |
private var singlelines:TimelineMax; |
13 |
|
14 |
//This timeline groups the main animation timeline and the single slides animations timeline
|
15 |
private var timeline:TimelineMax; |
Step 10: Setting up the Slides
The core of the microsite. I use 3 basic functions of this engine, but first imagine the timeline as if it was a real timeline.
- The first function is insert(), this inserts a tween in the current frame, meaning that every time you insert() you will be adding a tween to the frame you're working, making all of your inserts start at the same time.
- The second is append(), this method lets me add tweens to a timeline in a sequence.
- The third is appendMultiple(), this method lets me pass an array of tweens to start at the same time, in sequence or with delay, depending on how I set the params.
1 |
|
2 |
private function setupSlides():void { |
3 |
//populates the slides
|
4 |
slides = new Array(home_mc, about_mc, works_mc, contacts_mc) |
5 |
|
6 |
//sets the offset
|
7 |
offset = 110 |
8 |
|
9 |
//instantiates the timeline for the main slides
|
10 |
slideline = new TimelineMax() |
11 |
|
12 |
//instantiates the timeline for each of the slides
|
13 |
singlelines = new TimelineMax(); |
14 |
|
15 |
//instatiates the timeline that will contain the other 2 timelines
|
16 |
timeline = new TimelineMax(); |
17 |
|
18 |
var i:int = 0 |
19 |
while (i < slides.length) { |
20 |
//sets an index so i know in wich position the current slide is
|
21 |
slides[i].index = i |
22 |
//aligns the slides
|
23 |
slides[i].x = i * 650 + offset |
24 |
//creates the tweens and appends them to a timeline
|
25 |
slideline.insert(TweenLite.to(slides[i], 3, { x:slides[i].x - 650*3,ease:Linear.easeNone } )) |
26 |
//increments the i for the next loop
|
27 |
i++ |
28 |
}
|
29 |
|
30 |
//initial states
|
31 |
home_mc.text_mc.alpha = 0 |
32 |
home_mc.image_mc.alpha = 0 |
33 |
about_mc.text_mc.alpha = 0 |
34 |
works_mc.text_mc.alpha = 0 |
35 |
works_mc.image1_mc.alpha = 0 |
36 |
works_mc.image2_mc.alpha = 0 |
37 |
works_mc.image3_mc.alpha = 0 |
38 |
contacts_mc.text_mc.alpha = 0 |
39 |
|
40 |
//sequencing the animations
|
41 |
singlelines.append(TweenLite.to(home_mc.text_mc, 0.2, { alpha:1 } )) |
42 |
singlelines.append(TweenLite.to(home_mc.image_mc, 0.2, { alpha:1 } ), -0.1) |
43 |
singlelines.append(TweenLite.to(about_mc.text_mc, 0.2, { alpha:1 } ), 0.5) |
44 |
singlelines.append(TweenLite.to(works_mc.text_mc, 0.2, { alpha:1 } ),0.15) |
45 |
singlelines.append(TweenLite.to(works_mc.image1_mc, 0.2, { alpha:1 } ), 0.05) |
46 |
singlelines.append(TweenLite.to(works_mc.image2_mc, 0.2, { alpha:1 } ), 0.05) |
47 |
singlelines.append(TweenLite.to(works_mc.image3_mc, 0.2, { alpha:1 } ), 0.05) |
48 |
singlelines.append(TweenLite.to(contacts_mc.text_mc, 0.2, { alpha:1 } ),0.55) |
49 |
//makes both timelines active at the same time.
|
50 |
timeline.appendMultiple([slideline, singlelines]); |
51 |
//starts timeline paused
|
52 |
timeline.pause(); |
Step 11: Navigation Variables
We only need one variable, and it's for the slider to know how much to slide.
1 |
|
2 |
private var scroll_amount:Number; |
Step 12: Setting up Navigation
Now we'll set up the scroll_amount and associate a few listeners to some functions.
1 |
|
2 |
private function setupNavigation():void { |
3 |
|
4 |
//sets the scroll amount
|
5 |
scroll_amount = navigation_mc.width-navigation_mc.selection_mc.width |
6 |
|
7 |
navigation_mc.selection_mc.buttonMode = true |
8 |
|
9 |
//on Mouse Down calls downHandler function
|
10 |
navigation_mc.selection_mc.addEventListener(MouseEvent.MOUSE_DOWN, downHandler); |
11 |
|
12 |
//this listener is set on stage in case you drag out
|
13 |
stage.addEventListener(MouseEvent.MOUSE_UP, upHandler) |
14 |
|
15 |
//play, pause and rewind event handler associations
|
16 |
play_bt.addEventListener(MouseEvent.CLICK, playSlides) |
17 |
pause_bt.addEventListener(MouseEvent.CLICK, pauseSlides) |
18 |
rewind_bt.addEventListener(MouseEvent.CLICK, rewindSlides) |
19 |
|
20 |
}
|
Step 13: downHandler Function
This is the method that is called when mouse is down over the dragger. It activates the mouse move listener that tells the slides where to go. It also removes any listener that is associated with the player.
1 |
|
2 |
private function downHandler(e:MouseEvent):void { |
3 |
//makes sure that the slider update is off before making it draggable
|
4 |
setUpdateSlider(false) |
5 |
//adds a listener to mouse movement so every time the mouse moves it updates the navigation slides
|
6 |
stage.addEventListener(MouseEvent.MOUSE_MOVE, updateNavigationSlides) |
7 |
//starts drag of the slider
|
8 |
navigation_mc.selection_mc.startDrag(false, new Rectangle(0, 0, scroll_amount, 0)); |
9 |
//updates navigation 1 time
|
10 |
updateNavigationSlides(null) |
11 |
}
|
Step 14: upHandler Function
This is the method that is called when mouse is up. It just stops the drag and removes the listener.
1 |
|
2 |
private function upHandler(e:MouseEvent):void { |
3 |
//removes the listener on mouse movements
|
4 |
stage.removeEventListener(MouseEvent.MOUSE_MOVE, updateNavigationSlides) |
5 |
//stops the drag
|
6 |
navigation_mc.selection_mc.stopDrag() |
7 |
}
|
Step 15: updateNavigationSlides Function
I love how I can just "goto and stop" to a label or to a time in a completly virtual timeline:
1 |
|
2 |
private function updateNavigationSlides(e:MouseEvent):void { |
3 |
//goes to that part in time,
|
4 |
// the calculation is a simple proportion fraction between the x position of the selection and the current timeline duration
|
5 |
timeline.gotoAndStop(navigation_mc.selection_mc.x*timeline.totalDuration/scroll_amount) |
6 |
}
|
Step 16: Video
Using ActionScript animation as video is as easy as setting up a timescale and calling play(), pause() or reverse().
1 |
|
2 |
|
3 |
//sets slider to be updated and resumes tween
|
4 |
private function playSlides(e:MouseEvent):void { |
5 |
timeline.timeScale = 1 |
6 |
timeline.play(); |
7 |
setUpdateSlider(true) |
8 |
}
|
9 |
//removes slider to be updated and pauses tween
|
10 |
private function pauseSlides(e:MouseEvent):void { |
11 |
setUpdateSlider(false) |
12 |
}
|
13 |
//sets the timescale and reverses the tween
|
14 |
private function rewindSlides(e:MouseEvent):void { |
15 |
timeline.timeScale = 5 |
16 |
timeline.reverse(); |
17 |
setUpdateSlider(true) |
18 |
}
|
Step 17: Setting up Slider Updates
Since there are two methods of navigating this microsite we need to make sure one doesn't influence the other, which could cause bugs later on. So we need to setup a small setter to identify whether it will update the slider or not according to the timeline animation, instead of the opposite. For that we create a setUpdateSlider
1 |
|
2 |
|
3 |
private function setUpdateSlider(bool:Boolean) { |
4 |
//case false, checks to see if there is a listener, if true pauses animation and removes tween
|
5 |
if (timeline.hasEventListener(TweenEvent.UPDATE) && bool == false) { |
6 |
timeline.pause() |
7 |
timeline.removeEventListener(TweenEvent.UPDATE,updateNavigationSlider) |
8 |
}
|
9 |
|
10 |
//case true, checks to see if there's a listener, if false plays animation
|
11 |
if (!timeline.hasEventListener(TweenEvent.UPDATE) && bool == true) { |
12 |
timeline.resume(); |
13 |
timeline.addEventListener(TweenEvent.UPDATE,updateNavigationSlider) |
14 |
}
|
15 |
}
|
Step 18: Updating the Slider
This function is called everytime a tween event updates
1 |
|
2 |
|
3 |
private function updateNavigationSlider(e:TweenEvent):void { |
4 |
//does exactly the same as updateNavigationSlides, but inverts the fraction so that updates the selection_mc position
|
5 |
navigation_mc.selection_mc.x=timeline.currentTime*scroll_amount/timeline.totalDuration |
6 |
}
|
Step 19: Full Code
This function is called everytime a tween event updates:
1 |
|
2 |
|
3 |
package { |
4 |
|
5 |
import flash.display.MovieClip; |
6 |
import flash.events.MouseEvent; |
7 |
import flash.geom.Rectangle; |
8 |
import com.greensock.TweenLite; |
9 |
import com.greensock.TimelineMax; |
10 |
import com.greensock.events.TweenEvent; |
11 |
import com.greensock.easing.Linear; |
12 |
|
13 |
public class Main extends MovieClip{ |
14 |
|
15 |
public function Main():void { |
16 |
//This will setup the animation of the slides
|
17 |
setupSlides(); |
18 |
|
19 |
//This will setup the slides navigation
|
20 |
setupNavigation(); |
21 |
}
|
22 |
|
23 |
////////////////SLIDES/////////////////
|
24 |
|
25 |
//An array that will store the slides for easier access
|
26 |
private var slides:Array; |
27 |
|
28 |
//A Number that will set a margin for the slides
|
29 |
private var offset:Number |
30 |
|
31 |
//A timeline that will store the slide tweens
|
32 |
private var slideline:TimelineMax |
33 |
|
34 |
//A timeline that will store each slide tween
|
35 |
private var singlelines:TimelineMax; |
36 |
|
37 |
//This timeline groups the main animation timeline and the single slides animations timeline
|
38 |
private var timeline:TimelineMax; |
39 |
|
40 |
private function setupSlides():void { |
41 |
//populates the slides
|
42 |
slides = new Array(home_mc, about_mc, works_mc, contacts_mc) |
43 |
|
44 |
//sets the offset
|
45 |
offset = 110 |
46 |
|
47 |
//instantiates the timeline for the main slides
|
48 |
slideline = new TimelineMax() |
49 |
|
50 |
//instantiates the timeline for each of the slides
|
51 |
singlelines = new TimelineMax(); |
52 |
|
53 |
//instaciates the timeline that will contain the other 2 timelines
|
54 |
timeline = new TimelineMax(); |
55 |
|
56 |
var i:int = 0 |
57 |
while (i < slides.length) { |
58 |
//sets an index so i know in wich position the current slide is
|
59 |
slides[i].index = i |
60 |
//aligns the slides
|
61 |
slides[i].x = i * 650 + offset |
62 |
//creates the tweens and appends them to a timeline
|
63 |
slideline.insert(TweenLite.to(slides[i], 3, { x:slides[i].x - 650*3,ease:Linear.easeNone } )) |
64 |
//pauses the slideline so it won't start automatically
|
65 |
|
66 |
//increments the i for the next loop
|
67 |
i++ |
68 |
}
|
69 |
|
70 |
//initial states
|
71 |
home_mc.text_mc.alpha = 0 |
72 |
home_mc.image_mc.alpha = 0 |
73 |
about_mc.text_mc.alpha = 0 |
74 |
works_mc.text_mc.alpha = 0 |
75 |
works_mc.image1_mc.alpha = 0 |
76 |
works_mc.image2_mc.alpha = 0 |
77 |
works_mc.image3_mc.alpha = 0 |
78 |
contacts_mc.text_mc.alpha = 0 |
79 |
|
80 |
//sequencing the animations
|
81 |
singlelines.append(TweenLite.to(home_mc.text_mc, 0.2, { alpha:1 } )) |
82 |
singlelines.append(TweenLite.to(home_mc.image_mc, 0.2, { alpha:1 } ), -0.1) |
83 |
singlelines.append(TweenLite.to(about_mc.text_mc, 0.2, { alpha:1 } ), 0.5) |
84 |
singlelines.append(TweenLite.to(works_mc.text_mc, 0.2, { alpha:1 } ),0.15) |
85 |
singlelines.append(TweenLite.to(works_mc.image1_mc, 0.2, { alpha:1 } ), 0.05) |
86 |
singlelines.append(TweenLite.to(works_mc.image2_mc, 0.2, { alpha:1 } ), 0.05) |
87 |
singlelines.append(TweenLite.to(works_mc.image3_mc, 0.2, { alpha:1 } ), 0.05) |
88 |
singlelines.append(TweenLite.to(contacts_mc.text_mc, 0.2, { alpha:1 } ),0.55) |
89 |
|
90 |
timeline.appendMultiple([slideline, singlelines]); |
91 |
timeline.pause(); |
92 |
|
93 |
}
|
94 |
|
95 |
private function gotoLabel(label:String):void { |
96 |
slideline.tweenTo(label,{ease:Linear.easeInOut}); |
97 |
}
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
//////////////////NAVIGATION/////////////////////
|
103 |
|
104 |
private var scroll_amount:Number; |
105 |
|
106 |
private function setupNavigation():void { |
107 |
|
108 |
//sets the scroll ammount
|
109 |
scroll_amount = navigation_mc.width-navigation_mc.selection_mc.width |
110 |
|
111 |
navigation_mc.selection_mc.buttonMode = true |
112 |
|
113 |
//on Mouse Down calls downHandler function
|
114 |
navigation_mc.selection_mc.addEventListener(MouseEvent.MOUSE_DOWN, downHandler); |
115 |
|
116 |
//this listener is set on stage in case you drag out
|
117 |
stage.addEventListener(MouseEvent.MOUSE_UP, upHandler) |
118 |
|
119 |
//play, pause and rewind event handler associations
|
120 |
play_bt.addEventListener(MouseEvent.CLICK, playSlides) |
121 |
pause_bt.addEventListener(MouseEvent.CLICK, pauseSlides) |
122 |
rewind_bt.addEventListener(MouseEvent.CLICK, rewindSlides) |
123 |
|
124 |
}
|
125 |
|
126 |
private function downHandler(e:MouseEvent):void { |
127 |
//makes sure that the slider update is off before making it draggable
|
128 |
setUpdateSlider(false) |
129 |
//adds a listener to mouse movement so every time the mouse moves it updates the navigation slides
|
130 |
stage.addEventListener(MouseEvent.MOUSE_MOVE, updateNavigationSlides) |
131 |
//starts drag of the slider
|
132 |
navigation_mc.selection_mc.startDrag(false, new Rectangle(0, 0, scroll_amount, 0)); |
133 |
//updates navigation 1 time
|
134 |
updateNavigationSlides(null) |
135 |
}
|
136 |
|
137 |
private function upHandler(e:MouseEvent):void { |
138 |
//removes the listener on mouse movements
|
139 |
stage.removeEventListener(MouseEvent.MOUSE_MOVE, updateNavigationSlides) |
140 |
//stops the drag
|
141 |
navigation_mc.selection_mc.stopDrag() |
142 |
}
|
143 |
|
144 |
private function updateNavigationSlides(e:MouseEvent):void { |
145 |
//goes to that part in time, the calculation is a simple proportion fraction between the x position of the selection and the current timeline duration
|
146 |
timeline.gotoAndStop(navigation_mc.selection_mc.x*timeline.totalDuration/scroll_amount) |
147 |
}
|
148 |
|
149 |
//sets slider to be updated and resumes tween
|
150 |
private function playSlides(e:MouseEvent):void { |
151 |
timeline.timeScale = 1 |
152 |
timeline.play(); |
153 |
setUpdateSlider(true) |
154 |
}
|
155 |
//removes slider to be updated and pauses tween
|
156 |
private function pauseSlides(e:MouseEvent):void { |
157 |
setUpdateSlider(false) |
158 |
}
|
159 |
//sets the timescale and reverses the tween
|
160 |
private function rewindSlides(e:MouseEvent):void { |
161 |
timeline.timeScale = 5 |
162 |
timeline.reverse(); |
163 |
setUpdateSlider(true) |
164 |
}
|
165 |
|
166 |
|
167 |
private function setUpdateSlider(bool:Boolean) { |
168 |
//case false, checks to see if there is a listener, if true pauses animation and removes tween
|
169 |
if (timeline.hasEventListener(TweenEvent.UPDATE) && bool == false) { |
170 |
timeline.pause() |
171 |
timeline.removeEventListener(TweenEvent.UPDATE,updateNavigationSlider) |
172 |
}
|
173 |
|
174 |
//case true, checks to see if there's a listener, if false plays animation
|
175 |
if (!timeline.hasEventListener(TweenEvent.UPDATE) && bool == true) { |
176 |
timeline.resume(); |
177 |
timeline.addEventListener(TweenEvent.UPDATE,updateNavigationSlider) |
178 |
}
|
179 |
}
|
180 |
|
181 |
private function updateNavigationSlider(e:TweenEvent):void { |
182 |
//does exactly the same as updateNavigationSlides, but inverts the fraction so that updates the selection_mc position
|
183 |
navigation_mc.selection_mc.x=timeline.currentTime*scroll_amount/timeline.totalDuration |
184 |
}
|
185 |
}
|
186 |
}
|
Conclusion
Be it Timeline Lite, or Timeline Max, building interactive motion graphic experiences with them is really easy.
This tutorial only scratches the surface of what the class can do. It has a very flexible workflow, I'm still working my way through it, but I assure you that after playing around with this class and realizing how to think with it, you will understand my hype.
I hope you liked this tutorial, thanks for reading!