The Flash community is a generous one. So here’s my first small gift back to a community that has already given me quite a bit.
Scroll bars in Flash are largely taken for granted. Until you try to craft your own from scratch, you don’t realize the math and combination of mouse listeners that go into a seemingly simple but proper scroll bar. In frustration, some settle for an even more frustrating user experience with just arrows that scroll content a painfully small amount with each click. There is of course the Flash components scroll bar, but try to edit it’s look and feel and you’re presented with a limited and confusing interface. Although editing components in CS4 is notably easier than previous versions, I found a scroll bar class by fellow Flash blogger Kyle Brekke which did just what I needed it to. What I’ve added to Kyle’s framework is this:
- A stage presence in form of a blue box. Resize this box (presence_mc) and the scroll bar will adopt the appropriate dimensions.
- The container movie clip the scroll bar is dynamically added to allows you to use layers to cover and tween it’s properties. This is more adaptive than adding it to the stage.
- Added scroll track click and hold functionality (more like conventional scroll bars).
- No support for horizontal scrollbars. No one likes using them and it slims down the code.
A word of caution: If you’re using GreenSock’s TweenLite, make sure this scrollbar and your project use the same version.
Here’s the example:
And here’s the source:
/**
* Archonic Customizable Scrollbar Component
*
* Derived from Kyle Brekke's Scrollbar
* Derived from Li Jiansheng's Scollbar
*
* @author Joshua Mark
* @date 31/07/2009
* @version 1.0.0
* @website http://www.Archonic.com
*/
package com.archonic.utils {
import com.greensock.TweenLite;
import flash.display.*;
import flash.events.*;
import flash.geom.Rectangle;
import flash.geom.Point;
public class scrollbar extends MovieClip {
// Private Get/Set Type Constants
private const XY:String = "xy";
private const WH:String = "widthHeight";
private const TB:String = "topBottom";
// Speed of Content Scroll
private const arrowMove:Number = 10;
private const scrollSpeed:Number = 10;
private const transSpeed:Number = 0.1;//seconds to tween
// Assets used in Drawing the Class
private var _arrowProgress:arrowProgress = new arrowProgress();
private var _arrowRegress:arrowRegress = new arrowRegress();
private var _scrollTrack:scrollTrack = new scrollTrack();
private var _scrollButton:scrollButton = new scrollButton();
// Standard Vars Set in Constructor
private var _target:MovieClip;
private var _stage:Stage;
private var _container:MovieClip;
// Numbers used in Calculating Slide Ratio
private var trackTop:Number;
private var dragBot:Number;
private var trackRange:Number;
private var slideRect:Rectangle;
// Allow user to click and hold arrows and continue to scroll
private var isUp:Boolean = false;
private var isDown:Boolean = false;
private var trackDown:Boolean = false;
// Constructor
public function scrollbar(s:Stage, c:MovieClip, target:MovieClip, maskWidth:Number, maskHeight:Number):void {
_stage = s;
_container = c;
_target = target; // Set the _target
_target.startPoint = new Point(_target.x, _target.y);// Set the _target's original point
// Set positions of the assets (into scrollbar form)
initAssets();
// Sets all listeners in one place
initListeners();
// Find the sliding range with which to create the ratio
initTrack();
// Draws and sets the mask
initMask(maskWidth, maskHeight);
}
private function initAssets():void {
var targetWidth = _container.presence_mc.width;
var targetHeight = _container.presence_mc.height - (2*targetWidth);
_scrollTrack.width = targetWidth;
_scrollTrack.height = targetHeight;
_scrollButton.width = targetWidth;
_scrollButton.y += _scrollButton.height/2;//stops jumping when scrolling up with _scrollTrack
_arrowRegress.height = targetWidth;
_arrowRegress.width = targetWidth;
_arrowRegress.y = _arrowRegress.height * -1;
_arrowProgress.height = targetWidth;
_arrowProgress.width = targetWidth;
_arrowProgress.y += _scrollTrack.height;
// Set arrows as buttons
_arrowRegress.buttonMode = true;
_arrowProgress.buttonMode = true;
_scrollButton.buttonMode = true;
// Add assets to this class
addChild(_scrollTrack);
addChild(_scrollButton);
addChild(_arrowProgress);
addChild(_arrowRegress);
y = _container.presence_mc.y + _arrowRegress.height;
// Add this class to the timeline container
_container.addChild(this);
}
private function initListeners():void {
// Event listeners for dragging and scroll wheel
_scrollButton.addEventListener(MouseEvent.MOUSE_DOWN, dragScroll);
_scrollButton.addEventListener(MouseEvent.MOUSE_OVER, stopTrackScroll);
_stage.addEventListener(MouseEvent.MOUSE_UP, stopScroll);
_stage.addEventListener(MouseEvent.MOUSE_WHEEL,mouseWheelHandler);
// Event listeners for arrows
_arrowRegress.addEventListener(Event.ENTER_FRAME, _arrowRegressHandler);
_arrowRegress.addEventListener(MouseEvent.MOUSE_DOWN, upScroll);
_arrowRegress.addEventListener(MouseEvent.MOUSE_UP, stopScroll);
_arrowProgress.addEventListener(Event.ENTER_FRAME, _arrowProgressHandler);
_arrowProgress.addEventListener(MouseEvent.MOUSE_DOWN, downScroll);
_arrowProgress.addEventListener(MouseEvent.MOUSE_UP, stopScroll);
// Event listeners for track
_scrollTrack.addEventListener(Event.ENTER_FRAME, _trackHandler);
_scrollTrack.addEventListener(MouseEvent.MOUSE_DOWN, trackScroll);
_scrollTrack.addEventListener(MouseEvent.MOUSE_UP, stopScroll);
}
private function initTrack():void {
trackTop = _get(XY, _scrollTrack) + (_scrollButton.height/2);
trackRange = _get(WH, _scrollTrack) - _get(WH, _scrollButton);
dragBot = trackTop + trackRange;
slideRect = new Rectangle(0, trackTop, 0, trackRange);
}
private function initMask(w:Number, h:Number):void {
if (_target.mask == null) {
var square:Sprite = new Sprite();
square.graphics.beginFill(0xFF0000);
square.graphics.drawRect(_target.x, _target.y, w, h);
_stage.addChild(square);
_target.mask = square;
}
}
// Next 4 functions deal with hitting the up/down arrows on the scrollbar
private function upScroll(e:MouseEvent):void {
isUp = true;
}
private function downScroll(e:MouseEvent):void {
isDown = true;
}
private function _arrowRegressHandler(e:Event):void {
if (isUp) {
if ( _get(XY, _scrollButton) > trackTop ) {
_set(XY, _scrollButton, _get(XY, _scrollButton) - arrowMove);
if ( _get(XY, _scrollButton) < trackTop ) {
_set(XY, _scrollButton, trackTop);
}
startScroll();
}
}
}
private function _arrowProgressHandler(e:Event):void {
if (isDown) {
if ( _get(XY, _scrollButton) < dragBot ) {
_set(XY, _scrollButton, _get(XY, _scrollButton) + arrowMove);
if ( _get(XY, _scrollButton) > dragBot ) {
_set(XY, _scrollButton, dragBot);
}
startScroll();
}
}
}
private function _trackHandler(e:Event):void {
if((isUp || isDown) && trackDown) {
if(mouseY < _scrollButton.y) {
_arrowRegressHandler(null);
} else if (mouseY > _scrollButton.y) {
_arrowProgressHandler(null);
}
}
}
private function trackScroll(e:MouseEvent):void
{
isUp = true;
isDown = true;
trackDown = true;
}
private function stopTrackScroll(e:MouseEvent):void
{
trackDown = false;
}
// Next 3 functions deal with dragging and mouse wheel
public function dragScroll(e:MouseEvent):void {
_scrollButton.startDrag(false, slideRect);
stage.addEventListener(MouseEvent.MOUSE_MOVE, moveScroll);
}
public function mouseWheelHandler(e:MouseEvent):void {
if (e.delta < 0) { //Scrolling Down
if ( _get(XY, _scrollButton) < dragBot ) {
_set(XY, _scrollButton, _get(XY, _scrollButton) - (e.delta * scrollSpeed));
if ( _get(XY, _scrollButton) > dragBot ) {
_set(XY, _scrollButton, dragBot);
}
startScroll();
}
} else { // Scrolling Up
if ( _get(XY, _scrollButton) > trackTop ) {
_set(XY, _scrollButton, _get(XY, _scrollButton) - (e.delta * scrollSpeed));
if ( _get(XY, _scrollButton) < trackTop ) {
_set(XY, _scrollButton, trackTop);
}
startScroll();
}
}
}
// Replicates the startScroll function except it works during the drag
// because it runs off the MOUSE_MOVE event
public function moveScroll(e:MouseEvent):void {
startScroll();
}
private function stopScroll(e:MouseEvent):void {
isDown = false;
isUp = false;
_scrollButton.stopDrag();
stage.removeEventListener(MouseEvent.MOUSE_MOVE, moveScroll);
}
// Major controlling function
private function startScroll():void {
var ratio:Number = ( _get(WH, _target) - trackRange ) / trackRange;
var sPos:Number = ( (_get(XY, _scrollButton) - trackTop) * ratio ) - _get(XY, _target.startPoint);
TweenLite.to( _target, transSpeed, {y:-sPos} );
}
// Functions used to minimize code switch for verticle/horizontal bars
private function _set(type:String, targ:*, val:Number):void {
if (type == XY) {
targ.y = val;
} else if (type == WH) {
targ.height = val;
} else if (type == TB) {
targ.bottom = val;
}
}
private function _get(type:String, targ:*):Number {
if (type == XY) {
return targ.y;
} else if (type == WH) {
return targ.height;
} else if (type == TB) {
return targ.bottom;
}
return 0;
}
}
}
Download the Archonic scrollbar
The license is MIT open source – use it, distribute it, edit it. Enjoy!
Posted on Sunday, July 5th, 2009 at 11:05 am and is filed under Flash. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

