Make your own scroll bar in Flash MX

Introduction

In this tutorial you will learn how to make a scroll bar that's almost exactly like the scrollbar component in Flash MX. I decided to make my own because, as a programmer, I feel better writing my own code than using someone else's. My scroll bar is not quite as self-contained, meaning that it doesn't automatically "know" how tall it should be. But I believe it's more flexible and customizable, and it's smaller in file size than the MX component. Anyway, I hope you'll find use for it!



Let's get started.

<%BRK%> The text field and scroll track

Open a new file in MX.

At the root level of Scene 1 add two extra layers to make it a total of three. Name the top layer "functions", the one below it "actions" and the bottom one "movie clip" or whatever else fits your purposes. We'll add the code later.

Create a dynamic text field in the bottom layer. Use a font, color, and size of your choice and drag it out to display a maximum of about 5 or 6 lines or however many you like. For this example, we will use a maximum of 6 lines that can be displayed at the same time. Make sure that "multiline" is selected and that "selectable" is not selected in the text properties. Give the text field an instance name of "w_field" (short for "window field" in my example). Don't worry about assigning a variable name. Lastly, click on "Character..." and select whichever character sets you would like to include.

For the main movie clip that contains everything else, select the text field and press F8 to convert it to a symbol. Choose movie clip for the behavior, name the symbol "main_mc" and give it an instance name "main". Now go into main_mc and rename the layer with the text field "text" and add a few more layers from top to bottom: "labels", "actions", "functions", "scroll thumb", "scroll buttons", and "scroll track". Once we add these graphics, we'll put them in their respective layers. If you want to include any other graphics, such as a window, add layers for those elements too.
Add two frames to each layer of this movie clip. Label the first frame "no_scroll", the second one "scroll_loop", and the third frame doesn't need a label. In the second frame of the "scroll thumb", "scroll buttons", and "scroll track" layers, add a keyframe each.



For the scroll track, go to the second frame of the "scroll track" layer. Draw a rectangle of the width you want your scroll bar to be, and make sure that its height is about the same as your dynamic text field. Line it up just to the right of your text field. Convert it to a movie clip symbol and assign it an instance name of "scroll_track".

We'll also add some code to this movie clip. In the actions layer of the first frame, just put

stop();
In the second ("scroll_loop") frame, put
play();
We'll add a little more to this later. And in the last frame put
gotoAndPlay("scroll_loop");
Now go back to the root level one more time, and we'll add some code there. In the functions layer, put the following:

function showText(message) {
	main.w_field.text = message;
	if (main.w_field.maxscroll <%gt%> main.w_field.scroll) {
		main.gotoAndPlay("scroll_loop");
	}
}
And in the actions layer, put this code:
my_text = "Put lots of text here, or the scroll bar won't show up!";
showText(my_text);
stop();
We're setting this up in a way that the scroll bar is only visible when the text field has more lines than can be diplayed at one time.

<%BRK%> The scroll buttons

Go back inside your main movie clip.

For the arrow buttons, I just used the ones from the scrollbar component available in Flash MX and changed the colors a little bit. Feel free to design your own cool button(s). However, they should be the same width as the scroll track. Place them on the scroll track and line them up at top and bottom. Their instance names should be "up_arrow" and "down_arrow". The buttons should only be present on the second and third frames of the main mc.

The first frame of the functions layer is our designated functions frame. We won't put functions on any other frames in this movie clip. Right now we'll add the functions that scroll the text field when an arrow button is clicked:

function scrollIt() {
	w_field.scroll += pressed;
}

function stopScroll() {
	still_pressed = false;
	pressed = false;
}

function callBack() {
	if (pressed) {
		still_pressed = true;
	} else {
		still_pressed = false;
	}
	clearInterval(checkIfPressed);
}

up_arrow.onPress = function() {
	pressed = -1;
	if (w_field.scroll <%gt%> 1) {
		scrollIt();
	}
	checkIfPressed = setInterval(callBack, 500);
}

down_arrow.onPress = function() {
	pressed = 1;
	if (w_field.scroll <%lt%> w_field.maxscroll) {
		scrollIt();
	}
	checkIfPressed = setInterval(callBack, 500);
}

up_arrow.onRelease = down_arrow.onRelease = up_arrow.onReleaseOutside = down_array.onReleaseOutside = stopScroll;


Publish your movie and click the arrows - it works!

Also, to ensure that the text field keeps scrolling when an arrow is held down continuously, add this to the second frame above the play() action:
if (still_pressed) {
	scrollIt();
}
Next we'll add the scroll thumb. Ready?

<%BRK%> The scroll thumb

For the scroll thumb, just make a rectangle or square and turn it into a button. It should be the same width as the scroll track and arrow buttons. Give the button an instance name of "thumb_btn" and then press F8 again to create a movie clip with the button inside. Assign the movie clip the instance name "scroll_thumb". Also, make sure it is only present on the second and third frames, just like the arrow buttons and scroll track. Place it right below the up arrow button on the scroll track since that is the initial position of it.



To make the scroll thumb draggable and scroll the text field according to it, we'll need to add the following code. This is what we'll add to the designated functions frame:
scroll_thumb.initiate = function() {
	this.initiated = true;
	this.min_y = this._y;
	this.max_y = this.min_y + (scroll_track._height - up_arrow._height - this._height/2) + 1;
	this.total_travel = Math.abs(this.max_y - this.min_y);
	this.inc = Math.floor(this.total_travel/w_field.maxscroll);
}

scroll_thumb.drag = function() {
	this.startDrag(false, this._x, this.min_y, this._x, this.max_y);
	if (!this.org_y) {
		this.org_y = this._y;
	}
	dragging = true;
	this.onEnterFrame = whileDragging;
}

scroll_thumb.thumb_btn.onPress = function () {
	scroll_thumb.drag();
}

scroll_thumb.thumb_btn.onRelease = function () {
	dragging = false;
	pressed = false;
	still_pressed = false;
	scroll_thumb.stopDrag();
	scroll_thumb.onEnterFrame = null;
}

scroll_thumb.checkPos = function () {
	if (this._y <%gt%> this.max_y) {
			this._y = this.max_y;
	} else if (this._y <%lt%> this.min_y) {
			this._y = this.min_y;
	}
}

function whileDragging() {
	if (dragging) {
		var moved = this._y - this.org_y;
		if (Math.abs(moved) <%gt%>= this.inc) {
			if (moved <%gt%> 0) {
				pressed = 1;
			} else {
				pressed = -1;
			}
			this.org_y = this._y;
			scrollIt();
			if (Math.abs(this._y - this.max_y) <%lt%> 1) {
				w_field.scroll = w_field.maxscroll;
			} else if (Math.abs(this._y - this.min_y) <%lt%> 1) {
				w_field.scroll = 1;
			}
		} else {
			pressed = 0;
		}
	}
}
Also, to the existing scrollIt function we wrote earlier, we'll insert the following right below the "w_field.scroll += pressed;" line:
if (!dragging) {
	if (w_field.scroll <%lt%>= 1) {
		scroll_thumb._y = scroll_thumb.min_y;
	} else if (w_field.scroll <%gt%>= w_field.maxscroll){
		scroll_thumb._y = scroll_thumb.max_y;
	} else {
		scroll_thumb._y += scroll_thumb.inc*pressed;
		scroll_thumb.checkPos();
	}
}
Lastly, to first initiate the scroll thumb and to make the dragging work perfectly, add this in the second frame above the play() action:
if (!scroll_thumb.initiated) {
	scroll_thumb.initiate();
}
if (Math.abs(scroll_thumb._y - scroll_thumb.max_y) <%lt%> 1) {
	w_field.scroll = w_field.maxscroll;
} else if (Math.abs(scroll_thumb._y - scroll_thumb.min_y) <%lt%> 1) {
	w_field.scroll = 1;
}
I know, I know, it's a lot of code but we're almost done now. Now, the only thing we have left to do is make the scroll track clickable as well so that scrolling is also activated when you click somewhere on the scroll track between either arrow button and the scroll thumb. Let's do that!

<%BRK%> Finishing the scroll track

The scroll track is already in place and its instance name is "scroll_track", right? Good! Now let's add the following code to our functions frame:
scroll_track.useHandCursor = false;

scroll_track.onPress = function () {
	rec_y = _root._ymouse - this._parent._y;
	if (rec_y <%gt%> scroll_thumb._y) {
		pressed = 5;
	} else {
		pressed = -5;
	}
	scrollIt();
	checkIfPressed = setInterval(callBack, 500);
}

scroll_track.onRelease = scroll_track.onReleaseOutside = stopScroll;
One important thing to note here is that the number 5 in my code is one less than the maximum number of lines that can be displayed in the text field at one time. So if your text fields holds 20 lines, for example, you would use 19 and -19 in your code instead. And that's about it!

On the next page, I'll have a summary of all the code we need, and where it should be.

<%BRK%> To sum up

I will no reiterate the necessary blocks of code in their entirety. For more clarity, I am including some comments as well.

Top level

Frame 1 - functions:
// display text in text field; decide whether it needs scrolling
function showText(message) {
	main.w_field.text = message;
	if (main.w_field.maxscroll <%gt%> main.w_field.scroll) {
		main.gotoAndPlay("scroll_loop");
	}
}
Frame 1 - actions:
// change this variable to whatever suits your needs
my_text = "Put the text you want displayed in your text field here!
Make sure that it is plenty of text so you'll actually see the
scroll bar in action!";
showText(my_text);
stop();


Main movie clip (instance name: "main")

Frame 1 - functions:
// scrolls text field up or down
function scrollIt() {
	w_field.scroll += pressed;
	// make sure that scroll thumb always stays on track
	if (!dragging) {
		if (w_field.scroll <%lt%>= 1) {
			scroll_thumb._y = scroll_thumb.min_y;
		} else if (w_field.scroll <%gt%>= w_field.maxscroll){
			scroll_thumb._y = scroll_thumb.max_y;
		} else {
			scroll_thumb._y += scroll_thumb.inc*pressed;
			scroll_thumb.checkPos();
		}
	}
}
// called when pressing stops
function stopScroll() {
	still_pressed = false;
	pressed = false;
}
// after half a second check if button is still pressed
function callBack() {
	if (pressed) {
		still_pressed = true;
	} else {
		still_pressed = false;
	}
	clearInterval(checkIfPressed);
}
// arrow button functions:
up_arrow.onPress = function() {
	pressed = -1;
	if (w_field.scroll <%gt%> 1) {
		scrollIt();
	}
	checkIfPressed = setInterval(callBack, 500);
}
down_arrow.onPress = function() {
	pressed = 1;
	if (w_field.scroll <%lt%> w_field.maxscroll) {
		scrollIt();
	}
	checkIfPressed = setInterval(callBack, 500);
}
up_arrow.onRelease = down_arrow.onRelease = up_arrow.onReleaseOutside = down_array.onReleaseOutside = stopScroll;
// initialize scroll thumb by setting some variables
scroll_thumb.initiate = function() {
	this.initiated = true;
	this.min_y = this._y;
	this.max_y = this.min_y + (scroll_track._height - up_arrow._height - this._height) + 1;
	this.total_travel = Math.abs(this.max_y - this.min_y);
	this.inc = Math.floor(this.total_travel/w_field.maxscroll);
}
// scroll thumb drag function
scroll_thumb.drag = function() {
	this.startDrag(false, this._x, this.min_y, this._x, this.max_y);
	if (!this.org_y) {
		this.org_y = this._y;
	}
	dragging = true;
	this.onEnterFrame = whileDragging;
}
// scroll thumb button functions:
scroll_thumb.thumb_btn.onPress = function () {
	scroll_thumb.drag();
}
scroll_thumb.thumb_btn.onRelease = function () {
	dragging = false;
	pressed = false;
	still_pressed = false;
	scroll_thumb.stopDrag();
	scroll_thumb.onEnterFrame = null;
}
// make sure scroll thumb never goes beyond its max or min spots
scroll_thumb.checkPos = function () {
	if (this._y <%gt%> this.max_y) {
		this._y = this.max_y;
	} else if (this._y <%lt%> this.min_y) {
		this._y = this.min_y;
	}
}
// scroll thumb enterFrame function while being dragged
function whileDragging() {
	if (dragging) {
		var moved = this._y - this.org_y;
		if (Math.abs(moved) <%gt%>= this.inc) {
			if (moved > 0) {
				pressed = 1;
			} else {
				pressed = -1;
			}
			this.org_y = this._y;
			scrollIt();
			// make sure text field is in tune with thumb
			if (Math.abs(this._y - this.max_y) <%lt%> 1) {
				w_field.scroll = w_field.maxscroll;
			} else if (Math.abs(this._y - this.min_y) <%lt%> 1) {
				w_field.scroll = 1;
			}
		} else {
			pressed = 0;
		}

	}
}
// scroll track parameters & functions
scroll_track.useHandCursor = false;
scroll_track.onPress = function () {
	rec_y = _root._ymouse - this._parent._y;
	if (rec_y <%gt%> scroll_thumb._y) {
		pressed = 5;
	} else {
		pressed = -5;
	}
	scrollIt();
	checkIfPressed = setInterval(callBack, 500);
}
scroll_track.onRelease = scroll_track.onReleaseOutside = stopScroll;

Frame 1 - actions:
stop();
Frame 2 - actions:
if (!scroll_thumb.initiated) {
	scroll_thumb.initiate();
}
// keep scrolling if button or track is still pressed
if (still_pressed) {
	scrollIt();
}
// this ensures proper working of scroll thumb
if (Math.abs(scroll_thumb._y - scroll_thumb.max_y) <%lt%> 1) {
	w_field.scroll = w_field.maxscroll;
} else if (Math.abs(scroll_thumb._y - scroll_thumb.min_y) <%lt%> 1) {
	w_field.scroll = 1;
}
play();
Frame 3 - actions:
gotoAndPlay("scroll_loop");
That's it! If you need some more explanation of the code or you have any other questions or problems, feel free to email me!