Reusable AS 3.0 ScrollBar Class

October 25th, 2008 by Biro Barna

Hi everyone and welcome to the newly launched Wisebisoft WebLog.

If you have been visiting the old blog then you know that I had a reusable ActionScript 3.0 scrollbar class there which was a kinda out of date. Although I created a new version, I decided not to upload it there but wait until this new WebLog is launched.

I didn’t bother adding fancy graphics to it but of course it can be done. The scrollbar has additional features too that can be disabled or enabled, one such feature is the “blurring feature” which will blur the content on scroll. You can play around with these features yourself and even add new ones if you like.

You can download the working example from here: Custom ScrollBar

Here’s the source code of the class:

package com.wisebisoft.widgets
{
	import flash.display.MovieClip;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventPhase;
	import flash.events.MouseEvent;
	import flash.filters.BitmapFilterQuality;
	import flash.filters.BlurFilter;
	import flash.geom.Rectangle;
 
	/**
	 * 
	 * Author: Biro Barna ( http://www.wisebisoft.com/ )
	 * Created on: Wednesday, September 03, 2008, 4:22 AM (GTM +2:00) Minsk
	 * Description: Reusable ActionScript 3.0 ScrollBar class
	 * Usage: Simply instantiate the class and pass all the 5 required arguments ( and you can set 6 additional properties too ).
	 * Example: new CustomScrollBar(stage, content_mc, content_mask, scroll_track, scroll_btn, true, true, true, true, .2, 5);
	 * 
	 * ---------------- ONLY 5 PARAMETERS ARE REQUIRED FOR THE CLASS TO FUNCTION ----------------------
	 * 
	 * -- stage -- is your Stage object, it will help you prevent people from messing up certain features
	 * -- content_mc -- is the movie clip holding the content that you want to scroll
	 * -- content_mask -- is the movie clip that you'll use to mask content_mc movie clip
	 * -- scroll_track -- is the movie clip that plays the role of a track for the scroll_btn
	 * -- scroll_btn -- is the button that you can click + hold and drag to scroll your content_mc movie clip
	 * 
	 * -- -------------- ALL THE PARAMETERS AFTER THE 5TH PARAMETER ARE OPTIONAL ---------------------------------------
	 * 
	 * -- the following 4 parameters in the line enable or disable certain features like mouse wheel scrolling or blurring
	 * -- .3 -- is the easing that will be added to the content_mc movie clip; the lower the value is the more easing is applied
	 * -- 10 -- is the number of pixels the scroll_btn will move on the Y axis each time you scroll with the mouse wheel
	 * 
	 * */
 
	public class CustomScrollBar
	{
		private var content:MovieClip;
		private var contentMask:MovieClip;
		private var scrollTrack:MovieClip;
		private var scrollButton:MovieClip;
		private var useMouseWheel:Boolean;			// helps you enable or disable the mouse wheel scrolling feature
		private var enableJumping:Boolean;			// helps you enable or disable the "jumping feature"
		private var buttonModeEnabled:Boolean;		// helps you enable or disable the scroll button's buttonMode property
		private var easing:Number;
		private var scrollSpeed:Number;
		private var moveValue:Number;				// helps calculate the final moveSpeed value
		private var moveSpeed:Number;				// the speed with which the content will be scrolled
		private var currentContentY:Number;			// stores the current position of the content movie clip
		private var scrollTrackScaleY:Number;
		private var contentHeight:Number;
		private var scrollTrackHeight:Number;
		private var scrollButtonHeight:Number;
		private var contentMaskHeight:Number;
		private var movieStage:Stage;
		private var enterFrameActive:Boolean;		// it "signals" if the ENTER_FRAME event has been triggered or not
		private var allowRemove:Boolean;			// helps your function know when it is allowed to remove the ENTER_FRAME event
		private var blurEnabled:Boolean;			// enables or disables content blur on scroll
 
		// CLASS CONSTRUCTOR //
 
		public function CustomScrollBar(
 
							movieStage:Stage,
							content:MovieClip,
							contentMask:MovieClip, 
							scrollTrack:MovieClip,
							scrollButton:MovieClip,
							useMouseWheel:Boolean = true,		// mouse wheel scrolling is by default: ENABLED
							enableJumping:Boolean = true,		// the "jumping feature" is by default: ENABLED
							buttonModeEnabled:Boolean = true,	// scroll button's buttonMode property is by default: ENABLED
							blurEnabled:Boolean = false,		// content blurring on scroll event is by default: DISABLED
							easing:Number = .15,				// easing speed is by default set to: .15
							scrollSpeed:Number = 5				// scrolling speed is be default set to: 5
 
						   )
		{
			// store certain properties of the objects passed to the class constructor
			// in specific variables, increasing performance at the same time
 
			this.content = content;
			this.contentMask = contentMask;
			this.scrollTrack = scrollTrack;
			this.scrollButton = scrollButton;
			this.useMouseWheel = useMouseWheel;
			this.enableJumping = enableJumping;
			this.buttonModeEnabled = buttonModeEnabled;
			this.blurEnabled = blurEnabled;
			this.easing = easing;
			this.scrollSpeed = scrollSpeed;
			this.movieStage = movieStage;
 
			setHeights();
			setButtonMode();
			setContentMask();
			storeTrackScaleY();
			repositionObjects();
			addListeners();
		}
 
		// store the height of your objects
		private function setHeights():void
		{
			contentHeight = content.height;
			contentMaskHeight = contentMask.height;
			scrollTrackHeight = scrollTrack.height;
			scrollButtonHeight = scrollButton.height;
		}
 
		// enable or disable the buttonMode property of your scroll button
		private function setButtonMode():void
		{
			scrollButton.buttonMode = buttonModeEnabled;
		}
 
		// apply the mask movie clip to the content movie clip
		private function setContentMask():void
		{
			// cache both the content and content mask so that you'll
			// be able to apply masks that container an alpha channel too
 
			content.cacheAsBitmap = true;
			contentMask.cacheAsBitmap = true;
			content.mask = contentMask;
		}
 
		// reposition your objects so that everything will scroll correctly
		private function repositionObjects():void
		{
			contentMask.x = content.x;
			scrollButton.x = scrollTrack.x;
			content.y = contentMask.y = scrollTrack.y = scrollButton.y = 0;
		}
 
		// store the scaleY property of the scroll track movie clip
		private function storeTrackScaleY():void
		{
			scrollTrackScaleY = scrollTrack.scaleY;
		}
 
		// add all the event listeners that will be handling the scrolling
		private function addListeners():void
		{
			enterFrameActive = false;
 
			// check to see if the actual height of the content is more than the
			// height of the content mask; if the content height is less, that means that there
			// is no use for a scroll track and nor for a scroll button, so we'll make them invisible
			// ... otherwise, add all the required listeners that will handle the scrolling
 
			if (contentHeight <= contentMaskHeight)
			{
				scrollTrack.visible = false;
				scrollButton.visible = false;
			}
			else
			{
				scrollTrack.visible = true;
				scrollButton.visible = true;
 
				if (useMouseWheel == true)
				{
					content.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
				}
 
				if (enableJumping == true)
				{
					scrollTrack.addEventListener(MouseEvent.MOUSE_DOWN, scrollTrackMouseHandler);
				}
 
				scrollButton.addEventListener(MouseEvent.MOUSE_DOWN, startDragHandler);
				scrollButton.addEventListener(MouseEvent.MOUSE_UP, stopDragHandler);
				movieStage.addEventListener(MouseEvent.MOUSE_UP, stopDragHandler);
			}
		}
 
		// the moveContentHandler function is responsible for moving the content movie clip
		// some easing is added too ( just to make it look better ) and an optional blur filter
		private function moveContentHandler(event:Event):void
		{
			// store the current Y position of the content movie clip
			currentContentY = content.y;
 
			moveValue = (contentHeight - contentMaskHeight) / (scrollTrackHeight - scrollButtonHeight);
			moveSpeed = (scrollButton.y * (-moveValue) - content.y) * easing;
			content.y += moveSpeed;
 
			// if blurEnabled is set to "true" then it will apply a blur filter to the movie clip that is holding the content;
			// you can edit the filter parameters so that it will fit your needs; I tried keeping the blurring feature as
			// simple as possible, this feature was intended to be a "fun feature" than something advanced
 
			if (blurEnabled == true)
			{
				content.filters = [new BlurFilter(Math.abs(moveSpeed / 8), Math.abs(moveSpeed), BitmapFilterQuality.MEDIUM)];
			}
 
			removeContentEnterFrame();
		}
 
		// function responsible for moving the scroll button movie clip if the
		// visitor uses the mouse wheel to scroll the content movie clip
		private function mouseWheelHandler(event:MouseEvent):void
		{
			allowRemove = true;
			addContentEnterFrame();
 
			scrollButton.y += event.delta * (-scrollSpeed);
 
			if (scrollButton.y <= 0)
			{
				scrollButton.y = 0;
			}
			else if (scrollButton.y >= (scrollTrackHeight - scrollButtonHeight))
			{
				scrollButton.y = scrollTrackHeight - scrollButtonHeight;
			}
		}
 
		// function responsible for the "jumping feature", meaning that you can click any point on
		// the scroll track movie clip and the scroll button will be moved to that certain point, updating
		// the content movie clip's positions at the same time
		private function scrollTrackMouseHandler(event:MouseEvent):void
		{
			if (event.eventPhase == EventPhase.AT_TARGET)
			{
				addContentEnterFrame();
 
				// check for the coordinates of the visitor's CLICK event on the scroll track movie clip, if the coordinates are
				// close to the top of the scroll track then the scroll button will be moved to the top of the track...
				// ...else if the click event had place close to the bottom of the track then the scroll button will be placed at 
				// the bottom of the track... else, it will be moved to the exact point from where the visitor has fired the CLICK event
 
				if (event.target.mouseY * scrollTrackScaleY <= (scrollTrackHeight - scrollButtonHeight / 2) &&
					event.target.mouseY * scrollTrackScaleY >= (scrollButtonHeight / 2))
				{
					scrollButton.y = event.target.mouseY * scrollTrackScaleY  - scrollButtonHeight / 2;
				}
				else if (event.target.mouseY * scrollTrackScaleY <= (scrollButtonHeight / 2))
				{
					scrollButton.y = 0;
				}
				else if (event.target.mouseY * scrollTrackScaleY >= (scrollTrackHeight - scrollButtonHeight / 2))
				{
					scrollButton.y = scrollTrackHeight - scrollButtonHeight;
				}
			}
		}
 
		// function responsible for the drag feature of the scroll button
		private function startDragHandler(event:MouseEvent):void
		{
			allowRemove = false;
			addContentEnterFrame();
			scrollButton.startDrag(false, new Rectangle(scrollTrack.x, scrollTrack.y, 0,(scrollTrackHeight - scrollButtonHeight)));
		}
 
		// function responsible for stopping the scroll button from dragging once the visitor releases it
		private function stopDragHandler(event:MouseEvent):void
		{
			scrollButton.stopDrag();
			allowRemove = true;
		}
 
		// function responsible for adding the ENTER_FRAME event that is responsible
		// for moving ( positioning ) the content movie clip
		private function addContentEnterFrame():void
		{
			if (enterFrameActive == false)
			{
				enterFrameActive = true;
				content.addEventListener(Event.ENTER_FRAME, moveContentHandler);
			}
		}
 
		// this function will remove the ENTER_FRAME event once the content movie clip stops scrolling
		private function removeContentEnterFrame():void
		{
			if (currentContentY == content.y && allowRemove == true)
			{
				content.removeEventListener(Event.ENTER_FRAME, moveContentHandler);
				enterFrameActive = false;
				allowRemove = false;
			}
		}
 
		// this is a simple function that removes all the listeners from all the objects
		// essentially, this function will be of much help if you want to use the same scrollbar instance
		// for all the content that will be displayed in your project
		public function kill():void
		{
			content.removeEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
			scrollTrack.removeEventListener(MouseEvent.MOUSE_DOWN, scrollTrackMouseHandler);
			content.removeEventListener(Event.ENTER_FRAME, moveContentHandler);
			scrollButton.removeEventListener(MouseEvent.MOUSE_DOWN, startDragHandler);
			scrollButton.removeEventListener(MouseEvent.MOUSE_UP, stopDragHandler);
			movieStage.removeEventListener(MouseEvent.MOUSE_UP, stopDragHandler);
		}
 
		// a public function responsible for updating the scrollbar, adding the event listeners again to each object,
		// repositioning all the objects, storing new values and enabling or disabling certain scrollbar features; 
		// all this is done ONLY after we remove the current event listeners with the help of the kill(); function
		public function update():void
		{
			kill();			
			setHeights();
			setButtonMode();
			storeTrackScaleY();
			repositionObjects();
			addListeners();
		}
 
		// public setter methods that will help you update the scrollbar, enable of disable features and add new values
 
		public function set setEasing(value:Number):void { easing = value };
		public function set setScrollSpeed(value:Number):void { scrollSpeed = value };
		public function set setMouseWheel(value:Boolean):void { useMouseWheel = value };
		public function set setJumping(value:Boolean):void { enableJumping = value };
		public function set setScrollButtonMode(value:Boolean):void { buttonModeEnabled = value };
 
		// public getter methods that you'll most likely not use very often but it's good to have them there;
		// if you run into problems / bugs then you can try debugging your application with the help of these getter
		// methods ( but if you are more familiar with the built-in Flash / Flex debugger then it's much better to use that instead )
 
		public function get getEasing():Number { return easing };
		public function get getScrollSpeed():Number { return scrollSpeed };
		public function get getMouseWheel():Boolean { return useMouseWheel };
		public function get getJumping():Boolean { return enableJumping };
		public function get getScrollButtonMode():Boolean { return buttonModeEnabled };
	}
}

It’s as efficient as it can get and certain features can be changed at runtime too ( with the help of the defined setter functions ).

Enjoy and don’t forget to report the bugs you find.

Posted in ActionScript 3.0

4 Responses

  1. scotty

    this.buttonModeEnbaled = buttonModeEnbaled;

    should be enabled? not enbaled?

  2. Biro Barna

    Yes, you are right. I accidentally mistyped the name but luckily I continued using it trough the whole class so it should be working just fine.

    I’ll change it and upload the corrected version so that no one will stone me for bad typo.

    Thanks for pointing it out. :)

  3. Tc

    Hi there!
    I have some problems using your scrollbar but I honestly don’t know what is actually happening. I created a movieclip and inside it I have a content_mc a content_mask and then the scroll_button and the scroll_track. It is just as your example file. The only difference is that my stage is bigger than your example file’s stage and the whole movieclip is positioned in the bottom right hand corner. When I test my movie it simply doesn’t show anything. I did a quick test in your demo file by changing the stage size and positioning the movieclips to the size and position as mine are and it showed but not in the same exact position. What’s up with that? Any idea what this might be?
    Thanks anyway for such a cool class. It would be excellent if I got this to work nice!

  4. Tc

    never mind! it works perfectly! eheh, sorry about that! THANKS MAN, awesome job!

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.