Scrollbar in ActionScript 3.0 AS3 | Light weight and simple without flex
Published by Nicholas Dunbar on December 24th, 2012
I was annoyed that I have to rely on bloated Flex components to do simple stuff. I ported the kirupa AS2 scrollbar to AS3 and then added a few touches to make it a little more versatile. It is really just a starting point. For example, if you are using a very small scroll bar then when dragging too fast you can loose the focus on the scroll bar. Again, this is a simplified class for you to build on and to add features like the one I am suggesting. Enjoy!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package | |
{ | |
import flash.display.InteractiveObject; | |
import flash.display.Sprite; | |
import flash.events.Event; | |
import flash.events.EventDispatcher; | |
import flash.events.MouseEvent; | |
import flash.geom.Rectangle; | |
public class ScrollBarLite { | |
//controlable assets | |
//the dragable rectange in the center of the scroll bar that also shows the user where in the scrolling they are. | |
private var scrollFace:Sprite; | |
//the top button at the top of the scroll bar that moves scrolls the view to the top of the page | |
private var btnUp:InteractiveObject; | |
//the bottom button at the bottom of the scroll bar that moves the view to the bottom of the page | |
private var btnDown:InteractiveObject; | |
//the content that is being scrolled | |
private var contentMain:Sprite; | |
//the viewport through which the content is being viewed | |
private var maskedView:Sprite; | |
//the scroll bar | |
private var scrollTrack:Sprite; | |
//the area where the mouse must be over to pick up MOUSE_WHEEL events. | |
private var _mouseWheelTarget:Sprite; | |
//the external function that gets called to do scrolling | |
private var _onCustomContentScrollProxy:Function; | |
//numbers | |
private var scrollHeight:Number; | |
private var contentHeight:Number; | |
private var scrollFaceHeight:Number; | |
private var maskHeight:Number; | |
private var initPosition:Number; | |
private var initContentPos:Number; | |
private var finalContentPos:Number; | |
private var left:Number; | |
private var top:Number; | |
private var right:Number; | |
private var bottom:Number; | |
private var dy:Number; | |
private var speed:Number; | |
private var moveVal:Number; | |
private var scrollWheelSpeed:Number = 20; | |
private var minScrollFaceHeight:Number = 10; | |
private var orgScrollFaceHeight:Number; | |
/** | |
* Control class for controlling scroll bar graphics | |
* | |
* @param scrollFaceParam the dragable rectange in the center of the scroll bar that also shows the user where in the scrolling they are. | |
* @param btnUpParam the top button at the top of the scroll bar that moves scrolls the view to the top of the page | |
* @param btnDownParam the bottom button at the bottom of the scroll bar that moves the view to the bottom of the page | |
* @param contentMainParam the content that is being scrolled | |
* @param maskedViewParam the viewport through which the content is being viewed | |
* @param scrollTrackParam the scroll bar | |
* | |
*/ | |
public function ScrollBarLite(scrollFaceParam:Sprite, btnUpParam:InteractiveObject, btnDownParam:InteractiveObject, contentMainParam:Sprite, maskedViewParam:Sprite, scrollTrackParam:Sprite) | |
{ | |
var scrollTrackRect:Rectangle; | |
this.scrollFace = scrollFaceParam; | |
this.btnUp = btnUpParam; | |
this.btnDown = btnDownParam; | |
this.contentMain = contentMainParam; | |
this.maskedView = maskedViewParam; | |
this.scrollTrack = scrollTrackParam; | |
scrollHeight = scrollTrack.height; | |
contentHeight = contentMain.height; | |
maskHeight = maskedView.height; | |
orgScrollFaceHeight = scrollFace.height; | |
if (contentHeight < maskHeight) { | |
scrollFace.visible = false; | |
btnUp.mouseEnabled = false; | |
btnDown.mouseEnabled = false; | |
} else { | |
scrollFace.visible = true; | |
btnUp.mouseEnabled = true; | |
btnDown.mouseEnabled = true; | |
//change the size of the scroll dragger aka scrollFace based on the size of the content vs the viewport | |
scrollFace.height = orgScrollFaceHeight*(maskHeight/contentHeight); | |
if (minScrollFaceHeight > scrollFace.height){ | |
scrollFace.height = minScrollFaceHeight; | |
} | |
} | |
scrollFaceHeight = scrollFace.height; | |
initPosition = scrollFace.y = scrollTrack.y; | |
initContentPos = contentMain.y; | |
finalContentPos = maskHeight-contentHeight+initContentPos; | |
scrollTrackRect = scrollTrack.getBounds(scrollTrack.parent); | |
left = scrollTrackRect.x; | |
top = scrollTrack.y; | |
right = scrollTrackRect.x; | |
bottom = scrollTrack.height-scrollFaceHeight+scrollTrack.y; | |
dy = 0; | |
speed = 10; | |
moveVal = (contentHeight-maskHeight)/(scrollHeight-scrollFaceHeight); | |
//set up listeners | |
scrollFace.addEventListener(MouseEvent.MOUSE_DOWN, _onPressScrollFace); | |
scrollFace.addEventListener(MouseEvent.MOUSE_UP, _onMouseUpScrollFace); | |
btnUp.addEventListener(MouseEvent.MOUSE_DOWN, _onPressBtnUp); | |
btnUp.addEventListener(MouseEvent.MOUSE_OUT, _onDragOutBtnUp); | |
btnUp.addEventListener(MouseEvent.MOUSE_UP, _onUpBtnUp); | |
btnDown.addEventListener(MouseEvent.MOUSE_DOWN, _onPressBtnDown); | |
btnDown.addEventListener(MouseEvent.MOUSE_OUT, _onDragOutBtnDown); | |
btnDown.addEventListener(MouseEvent.MOUSE_UP, _onUpBtnDown); | |
//make sure scrollFace is centered over the scrollTrack, you may want to comment this out if you are doing something weird with your scrollFace graphics | |
scrollFace.x = scrollTrackRect.x+( (scrollTrack.width - scrollFace.width)/2 ); | |
} | |
/** | |
* call this when ever the size of the content changes to that the scroll bar can update | |
* @param artificialHeight if you dont want to base the scroll bar on conent height and instead some other value | |
* | |
*/ | |
public function onContentSizeChange(artificialHeight:Number = -1):void{ | |
scrollHeight = scrollTrack.height; | |
if (artificialHeight != -1){ | |
contentHeight = artificialHeight; | |
} else { | |
contentHeight = contentMain.height; | |
} | |
maskHeight = maskedView.height; | |
if (contentHeight < maskHeight) { | |
scrollFace.visible = false; | |
btnUp.mouseEnabled = false; | |
btnDown.mouseEnabled = false; | |
scrollFace.height = orgScrollFaceHeight; | |
scrollFace.y = scrollTrack.y; | |
contentMain.y = initContentPos; | |
_removeListener(_mouseWheelTarget, MouseEvent.MOUSE_WHEEL, _onMouseWheel); | |
_removeListener(_mouseWheelTarget, MouseEvent.MOUSE_OUT, _onMouseUpScrollFace); | |
} else { | |
scrollFace.visible = true; | |
btnUp.mouseEnabled = true; | |
btnDown.mouseEnabled = true; | |
//change the size of the scroll dragger aka scrollFace based on the size of the content vs the viewport | |
scrollFace.height = orgScrollFaceHeight*(maskHeight/contentHeight); | |
if (minScrollFaceHeight > scrollFace.height){ | |
scrollFace.height = minScrollFaceHeight; | |
} | |
if ( !_mouseWheelTarget.hasEventListener(MouseEvent.MOUSE_WHEEL) ){ | |
_mouseWheelTarget.addEventListener(MouseEvent.MOUSE_WHEEL, _onMouseWheel); | |
_mouseWheelTarget.addEventListener(MouseEvent.MOUSE_OUT, _onMouseUpScrollFace); | |
} | |
} | |
scrollFaceHeight = scrollFace.height; | |
bottom = scrollTrack.height-scrollFaceHeight+scrollTrack.y; | |
finalContentPos = maskHeight-contentHeight+initContentPos; | |
moveVal = (contentHeight-maskHeight)/(scrollHeight-scrollFaceHeight); | |
} | |
/** | |
* run this function if you want to enable the mouse wheel when the mouse is over the target area. | |
* @param target the area that picks up MOUSE_WHEEL events | |
* | |
*/ | |
public function registerMouseWheelTarget(target:Sprite):void{ | |
target.addEventListener(MouseEvent.MOUSE_WHEEL, _onMouseWheel); | |
target.addEventListener(MouseEvent.MOUSE_OUT, _onMouseUpScrollFace); | |
_mouseWheelTarget = target; | |
} | |
/** | |
* if you don't want the scroll bar to work by changing the y value of the content and instead do something else you can register your own handler | |
* @param callback should look something like the following onScrollCallBack(xLocOfContent:Number) | |
* | |
*/ | |
public function registerCustomContentScroll(callback:Function):void{ | |
_onCustomContentScrollProxy = callback; | |
} | |
/** | |
* Removes all listeners that are currently running so that unused scroll bar components can be garbage collected. | |
*/ | |
public function destroy():void{ | |
_removeListener(scrollFace, MouseEvent.MOUSE_DOWN, _onPressScrollFace); | |
_removeListener(scrollFace, MouseEvent.MOUSE_UP, _onMouseUpScrollFace); | |
_removeListener(btnUp, MouseEvent.MOUSE_DOWN, _onPressBtnUp); | |
_removeListener(btnUp, MouseEvent.MOUSE_OUT, _onDragOutBtnUp); | |
_removeListener(btnUp, MouseEvent.MOUSE_UP, _onUpBtnUp); | |
_removeListener(btnUp, Event.ENTER_FRAME, _onEnterFrameBtnUp); | |
_removeListener(btnDown, MouseEvent.MOUSE_DOWN, _onPressBtnDown); | |
_removeListener(btnDown, MouseEvent.MOUSE_OUT, _onDragOutBtnDown); | |
_removeListener(btnDown, MouseEvent.MOUSE_UP, _onUpBtnDown); | |
_removeListener(btnDown, Event.ENTER_FRAME, _onEnterFrameBtnDown); | |
_removeListener(_mouseWheelTarget, MouseEvent.MOUSE_WHEEL, _onMouseWheel); | |
_removeListener(_mouseWheelTarget, MouseEvent.MOUSE_OUT, _onMouseUpScrollFace); | |
} | |
private function _onMouseWheel(e:MouseEvent):void{ | |
var d:Number; | |
var delta:Number = e.delta; | |
if (delta > 1){ | |
delta = 1; | |
} | |
if (delta < -1){ | |
delta = -1; | |
} | |
d = -delta * scrollWheelSpeed; | |
if (d > 0) { | |
scrollFace.y = Math.min(bottom, scrollFace.y+d); | |
} | |
if (d < 0) { | |
scrollFace.y = Math.max(top, scrollFace.y+d); | |
} | |
_updateContentPos(); | |
} | |
private function _updateContentPos():void { | |
dy = Math.abs(initPosition-scrollFace.y); | |
if (_onCustomContentScrollProxy == null){ | |
contentMain.y = Math.round(dy*-1*moveVal+initContentPos); | |
} else { | |
try{ | |
_onCustomContentScrollProxy(Math.round(dy*-1*moveVal+initContentPos)); | |
} catch(e:Error){ | |
trace("_onCustomContentScrollProxy callback failed : "+e.message); | |
} | |
} | |
} | |
private function _onPressScrollFace(e:MouseEvent):void { | |
var currPos:Number = scrollFace.y; | |
//startDrag(this, false, left, top, right, bottom); | |
scrollFace.startDrag( false, new Rectangle( scrollTrack.x+( (scrollTrack.width-scrollFace.width)/2 ), top, 0, bottom-top ) ); | |
scrollFace.addEventListener(MouseEvent.MOUSE_MOVE, _onMouseMoveScrollFace); | |
} | |
private function _onMouseMoveScrollFace(e:MouseEvent):void{ | |
//dy = Math.abs(initPosition-scrollFace.y); | |
//contentMain.y = Math.round(dy*-1*moveVal+initContentPos); | |
_updateContentPos(); | |
} | |
private function _onDragOutBtnDown(e:MouseEvent):void { | |
btnDown.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnDown); | |
} | |
private function _onDragOutBtnUp(e:MouseEvent):void { | |
btnUp.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnUp); | |
} | |
private function _onMouseUpScrollFace(e:MouseEvent):void { | |
scrollFace.stopDrag(); | |
//delete this.onMouseMove; | |
//trace(e.relatedObject); | |
scrollFace.removeEventListener(MouseEvent.MOUSE_MOVE, _onMouseMoveScrollFace); | |
} | |
private function _onPressBtnUp(e:MouseEvent):void { | |
btnUp.addEventListener(Event.ENTER_FRAME, _onEnterFrameBtnUp); | |
} | |
private function _onUpBtnUp(e:MouseEvent):void { | |
btnUp.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnUp); | |
} | |
private function _onEnterFrameBtnUp(e:Event):void { | |
if (contentMain.y+speed<maskedView.y) { | |
if (scrollFace.y<=top) { | |
scrollFace.y = top; | |
} else { | |
scrollFace.y -= speed/moveVal; | |
} | |
//contentMain.y += speed; | |
} else { | |
scrollFace.y = top; | |
//contentMain.y = maskedView.y; | |
//delete this.onEnterFrame; | |
btnUp.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnUp); | |
} | |
_updateContentPos(); | |
} | |
private function _onPressBtnDown(e:MouseEvent):void { | |
btnDown.addEventListener(Event.ENTER_FRAME, _onEnterFrameBtnDown); | |
} | |
private function _onUpBtnDown(e:MouseEvent):void { | |
btnDown.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnDown); | |
} | |
private function _onEnterFrameBtnDown(e:Event):void{ | |
if ( contentMain.y-speed > finalContentPos ) { | |
if (scrollFace.y >= bottom) { | |
scrollFace.y = bottom; | |
} else { | |
scrollFace.y += speed/moveVal; | |
} | |
//contentMain.y -= speed; | |
} else { | |
scrollFace.y = bottom; | |
//contentMain.y = finalContentPos; | |
//delete this.onEnterFrame; | |
btnDown.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnDown); | |
} | |
_updateContentPos(); | |
} | |
private function _removeListener(object:EventDispatcher, eventStr:String, callback:Function):void{ | |
if ( object && object.hasEventListener(eventStr) ){ | |
object.removeEventListener(eventStr, callback); | |
} | |
} | |
} | |
} | |