Shawnblais.com v2

The Lab

Simple Scroller Class AS2

with one comment

This scroller was written for an emergency situation at my last job, our 3rd party scroller from Extend Studios had a bug in in that caused the newest version of the Flash player to instantly crash our sites. This was a major problem, as we had already implemented about 35 sites using this plugin!

My Job was to create a chuck of inline code, which followed the function signature of our 3rd party component, and which would work with the existing MovieClip assets embedded into each movie (scroll_knob, scroll_track, scroll_button).

The end result is a loadScroller function, which can take a direct reference to an on-Stage movieClip, or a string containing the Linkage Name, and will create a very nice, smooth scroll bar for that clip. It features auto-resizing so it works great with dynamic text, and it can also be called from any other movieclip in your site. So you set this up once, and then you can call it from anywhere to easily scroll any movieclip you wish!

Unfortunately I couldn’t find the very latest version of the scroller, so there are a couple small bugs in this code which I don’t have time to fix right now:

Known Bugs:

  • Multiple mouse-wheel listeners are created when repeatedly instanciating scrollers. This cause the scroll amount of the mouse wheel to gradually increase.
  • unload function need to be updated to remove the mask, and kill the mouse wheel listener

Requirements:

  • Your movie must have 3 assets exported for actionscript: “btnl_up”, btnr_up”, “knob_up”, “track_up”

Example usage:

loadScroller(”mcContentText”, width,  height, x,  y);

loadScroller(”mcContentText”, placeHolder_mc._width, placeHolder_mc._height, placeHolder_mc._x, placeHolder_mc._y);

Inline Script:

///////////////////////////////////////////////////////////////////////////////////////////
// Scroller - v1.1
// 		This can be called by passing a string reference to an embedded movieClip, or by directly passing an instance from the stage.
//		The last parameter 'source_mc' is optional, to be used when	loading a scroller from inside another movieClip.
//
//		These references are created within the source movieclip:
//			source_mc.scroller
//			source_mc.scroller.scrollbar
//			source_mc.scroller.content
//
//		Scrolling can also be controlled using scroller.scrollTo(scrollOffset, easeIn). easeIn defaults to false.
//		Remove scrollbar by calling scrollbar.unload()
//////////////////////////////////////////////////////////////////////////////////////////

	import mx.transitions.easing.*;
	import mx.transitions.Tween;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	import flash.display.BitmapData;
	import flash.filters.GlowFilter;

	//SETUP
	var bottomPadding = 25; //Add a bit of extra space at the bottom of a clip
	var easingLevel = 5; //Easing duration (in frames). 0 = none
	var clickStep = 25; //Number of pixels to jump when mouse wheel, scroll track, or scroll buttons are pressed
	var glowColor = 0xFFFFFF; //Color of mouse-over glow effect
	var glowAlpha = 1; //Alpha level of glow-effect, between 0 - 1
	var loadDelay = .35; //Time (in ms) to wait until checking the height of the target_mc
	var mouseWheel = true; //Enable mouse wheel support
	var autoResize = true; //Automatically adjust scroll bar onEnterFrame
	var trackGlow = true; //Add glow effect to scroll track
	var knobOffset:Number = 5; //Tiling offset when creating the scrollKnob, in pixels. The source MC should be taller than this*2
	var skinPrefix = ""; //Prefix for skins movieclips
	var debugTrace = false; //Throws a trace on scroll so you know you're using the new scroller (temp)

	function loadScroller(instanceName, contentWidth, contentHeight, xPos, yPos, _source_mc){

	//Support backwards compatability, if source_mc is "Stage" get the intended object
		if (_source_mc == "Stage") instanceName = this[instanceName];

	//For source_mc to be valid, it should have a parent. If not, it's invalid so use 'this' instead.
		var source_mc = (_source_mc._parent) ? _source_mc : this;

	//Remove any existing scroll areas
		source_mc.scroller.removeMovieClip();

	//Create new scroll container
		var scroller = source_mc.createEmptyMovieClip("scroller",source_mc.getNextHighestDepth());
		source_mc.scroller = scroller;

	//If instance name has a parent, it must be on the stage...
		if(instanceName._parent)
			scroller.content = instanceName; //...create a reference to the source clip
	//Otherwise it's a Linkage Name from the Library...
		else
			scroller.content = scroller.attachMovie(instanceName, "content", 1);

	//Position and size
		scroller.content._x = int(xPos);
		scroller.content._y = int(yPos);
		scroller.baseWidth = int(contentWidth);
		scroller.baseHeight = int(contentHeight);

	//Create Mask
		createMask(scroller.content, int(contentWidth), int(contentHeight));

		setTimeout(function(){
			attachScrollbar(scroller, source_mc);
		}, loadDelay);
	}

	function attachScrollbar(_scroller, _source_mc){

		var source_mc = (_source_mc) ? _source_mc : this;

	//Find scroll container
		var scroller = _scroller;
		var scrollTarget_mc = scroller.content;

		//Create scroller container and position
			scroller.scrollbar.removeMovieClip();
			scrollbar = scroller.createEmptyMovieClip("scrollbar", scroller.getNextHighestDepth());
			scrollbar._x = int(scrollTarget_mc._x + scroller.baseWidth);

			//Save reference to scrollbar in source movieclip
			scroller.scrollbar = scrollbar;

			//Determine whether to hide or show the scrollbar
			scroller.scrollbar._visible = (scroller.content._height > scroller.baseHeight) ? true : false;

			//If a scrollbar has already been added...
			if(scroller.targetMin != undefined){
				//...save current y position of scrolled content
				var currentOffset = scrollTarget_mc._y;
				//Temporarily set content back to original position, to simplify calculations
				scrollTarget_mc._y = scroller.targetMin;
			}

			scrollbar._y = int(scrollTarget_mc._y);

		//Attach Buttons
			track_mc = scrollbar.attachMovie(skinPrefix + "track_up", "track_mc", 0);
			knobSource_mc = scrollbar.attachMovie(skinPrefix + "knob_up", "knobSource_mc", 1);
			buttonUp_mc = scrollbar.attachMovie(skinPrefix + "btnr_up", "buttonUp_mc", 2);
			buttonDown_mc = scrollbar.attachMovie(skinPrefix + "btnl_up", "buttonDown_mc", 3);

		//Size track
			track_mc._height = scroller.baseHeight;

		//Position Top Button
			buttonDown_mc._y = track_mc._height - buttonDown_mc._height;

		//Determine final knobHeight
			if(scrollTarget_mc._height < track_mc._height)
				knobHeight = track_mc._height - buttonUp_mc._height * 2;
			else {
				knobHeight = track_mc._height / (scrollTarget_mc._height/(track_mc._height - buttonUp_mc._height * 2));
				if(knobHeight < 20) knobHeight = 20;
			}

		//Create container for final Scroll Knob
			var knob_mc = scrollbar.createEmptyMovieClip("knob_mc", scrollbar.getNextHighestDepth());

		//Draw the Top 5px and add to knob_mc
			var knobTop_mc = knob_mc.createEmptyMovieClip("knobTop_mc", knob_mc.getNextHighestDepth());
			bmpData = new BitmapData(knobSource_mc._width, knobOffset, true, 0xFFFFFF);
			bmpData.draw(knobSource_mc, new Matrix(), null, null, new Rectangle(0, 0, knobSource_mc._width, knobOffset));
			knobTop_mc.attachBitmap(bmpData, knobTop_mc.getNextHighestDepth(), true, true);

		//Draw the Middle and add to knob_mc
			var knobMiddle_mc = knob_mc.createEmptyMovieClip("knobMiddle_mc", knob_mc.getNextHighestDepth());
			bmpData = new BitmapData(knobSource_mc._width, knobSource_mc._height-(knobOffset*2), true, 0xFFFFFF);
			bmpData.draw(knobSource_mc, new Matrix(1,0,0,1,0,-knobOffset), null, null, new Rectangle(0, 0, knobSource_mc._width, knobSource_mc._height-knobOffset*2),true);
			knobMiddle_mc.attachBitmap(bmpData, knobMiddle_mc.getNextHighestDepth(), true, true);
			knobMiddle_mc._y = knobOffset-1;
			knobMiddle_mc._height = knobHeight - (knobOffset*2 - 1);
			knobSource_mc._visible = false;
			knob_mc._y = buttonUp_mc._height;

		//Draw the bottom 5px and add to knob_mc
			var knobBottom_mc = knob_mc.createEmptyMovieClip("knobBottom_mc", knob_mc.getNextHighestDepth());
			bmpData = new BitmapData(knobSource_mc._width, knobOffset, true, 0xFFFFFF);
			bmpData.draw(knobSource_mc,  new Matrix(1,0,0,1,0,-knobSource_mc._height + knobOffset));
			knobBottom_mc.attachBitmap(bmpData, knobBottom_mc.getNextHighestDepth(), true, true);
			knobBottom_mc._y = knobHeight - (knobOffset + 1);

		//Now draw a bitmap copy of the entire knob, and use that instead of the 3 seperate clips
		//(looks better when scaling the movie)
			var knobBitmap_mc = knob_mc.createEmptyMovieClip("knobBitmap_mc", knob_mc.getNextHighestDepth());
			bmpData = new BitmapData(knob_mc._width, knob_mc._height, true, 0xFFFFFF);
			bmpData.draw(knob_mc);
			knobBitmap_mc.attachBitmap(bmpData, knobBitmap_mc.getNextHighestDepth(), true, true);
			//Remove the 3 source clips
			knobBottom_mc.removeMovieClip();
			knobMiddle_mc.removeMovieClip();
			knobTop_mc.removeMovieClip();

		//Init scrolling vars, save them inside the scroller so they can be easily referenced
			scroller.targetBaseY = scrollTarget_mc._y;
			scroller.scrollMin = buttonUp_mc._height;
			scroller.scrollMax = scrollbar._height - buttonUp_mc._height - knob_mc._height;

			scroller.mouseIsDown = false;
			scroller.dragOffset;

			scroller.targetHeight = scrollTarget_mc._height;
			scroller.targetMin = scrollTarget_mc._y;
			scroller.targetMax = scrollTarget_mc._y - scroller.targetHeight + scroller.baseHeight - bottomPadding;

			scroller.totalHeight = scroller.targetMin - scroller.targetMax;
			scroller.trackRatio = scroller.totalHeight/track_mc._height;

		//Check offset to see if the target need to be restored to it's original scroll position
		if(currentOffset != undefined){
			//Determing knob position
			offsetHeight = scroller.totalHeight - (currentOffset - scroller.targetMax);
			//Position knob and content
			scroller.content._y = currentOffset;
			knob_mc._y = scroller.scrollMin + (scroller.scrollMax-scroller.scrollMin) * (offsetHeight / scroller.totalHeight);
		}

		//Setup Scroll-Knob
			knob_mc.onPress = function() {
				scroller.mouseIsDown = true;
				scroller.dragOffset = this._ymouse;
			};

			knob_mc.onRelease = knob_mc.onReleaseOutside=function () {
				scroller.mouseIsDown = false;
			};

			knob_mc.onMouseMove = function() {
				if (scroller.mouseIsDown) {
					knob_mc._y += knob_mc._ymouse-scroller.dragOffset;
					scroller.checkBounds();
					updateAfterEvent();
					if(debugTrace) trace("Sitewyze Scroller v.1.1"); //(temp)
				}
			};

			knob_mc.onEnterFrame = function(){
				targetY = (scroller.targetMax-scroller.targetMin)*((knob_mc._y-scroller.scrollMin)/(scroller.scrollMax-scroller.scrollMin))+scroller.targetMin;

				if(!easingLevel)
					scrollTarget_mc._y += targetY-scrollTarget_mc._y;
				else
					scrollTarget_mc._y += Math.ceil((targetY-scrollTarget_mc._y)/easingLevel);

				if(scroller.content._height != scroller.targetHeight && autoResize == true){
					attachScrollbar(scroller, _root.scrollSkin, scroller._parent);
					updateAfterEvent();
				}
			};

			//Create Glow RollOver Effect for Knob
			knob_mc.onRollOver = knob_mc.onDragOver = function(){
				dummyTween = new Tween({position:0}, "_alpha", Regular.easeOut, 0, 1, .35, true);
				var thumb_mc = this;
				dummyTween.onMotionChanged = function(){
					thumb_mc.filters = [new GlowFilter(glowColor, this.position/2, 2, 2, glowAlpha, 3, false)];
				}
			}

			knob_mc.onRollOut = knob_mc.onDragOut = function(){
				dummyTween = new Tween({position:0}, "_alpha", Regular.easeOut, 1, 0, .35, true);
				var thumb_mc = this;
				dummyTween.onMotionChanged = function(){
					thumb_mc.filters = [new GlowFilter(glowColor, this.position/2, 3, 3, glowAlpha, 3, false)];
				}
			}

			//Apply same GlowEffect to the other buttons
			if(trackGlow) track_mc.onRollOver = track_mc.onDragOver = knob_mc.onDragOver;
			buttonUp_mc.onRollOver = buttonUp_mc.onDragOver = knob_mc.onDragOver;
			buttonDown_mc.onRollOver = buttonDown_mc.onDragOver = knob_mc.onDragOver;

			if(trackGlow) track_mc.onRollOut = track_mc.onDragOut = knob_mc.onRollOut;
			buttonUp_mc.onRollOut = buttonUp_mc.onDragOut = knob_mc.onRollOut;
			buttonDown_mc.onRollOut = buttonDown_mc.onDragOut = knob_mc.onRollOut;

		//Handle Mouse-Wheel
			mouseHandle = new Object();
			if(mouseWheel){
				mouseHandle.onMouseWheel = function(delta) {
					if (scroller.content.hitTest(_root._xmouse, _root._ymouse)) {
						knob_mc._y += (delta < 0) ? clickStep/scroller.trackRatio : -(clickStep/scroller.trackRatio); 						scroller.checkBounds(); 						updateAfterEvent(); 					} 				}; 			} 			Mouse.addListener(mouseHandle); 			 		//Handle Track Press 			track_mc.onPress = function(){ 				//If they clicked above the knob move content up, else move it down. 				scroller.scrollbar.knob_mc._y += (scroller.scrollbar._ymouse > scroller.scrollbar.knob_mc._y) ? clickStep/scroller.trackRatio : -(clickStep/scroller.trackRatio);
				scroller.checkBounds();
				updateAfterEvent();
			};

			buttonUp_mc.onPress = track_mc.onPress;
			buttonDown_mc.onPress = track_mc.onPress;

		//Function to checkBounds
		scroller.checkBounds = function(){
			if (this.scrollbar.knob_mc._y < this.scrollMin)  				this.scrollbar.knob_mc._y = this.scrollMin; 			else if (this.scrollbar.knob_mc._y > this.scrollMax)
				this.scrollbar.knob_mc._y = this.scrollMax;
		};

		//Function to manually scroll
		scroller.scrollTo = function(scrollToY, easeIn){
			//Determing knob position
			totalHeight = scroller.targetMin - scroller.targetMax;
			offsetHeight = totalHeight - (-(scrollToY-scroller.targetMin) - scroller.targetMax);
			//Position knob and content
			knob_mc._y = scroller.scrollMin + (scroller.scrollMax-scroller.scrollMin) * (offsetHeight / totalHeight);
			scroller.checkBounds();
			if(!easeIn) scroller.content._y = -(scrollToY-scroller.targetMin);
		};

		scroller.unload = function() {
			this.removeMovieClip();
		}
	}

//Simplified create mask function (temp)
	function createMask(clip_mc, _maskWidth, _maskHeight){
		mask_mc = clip_mc._parent.createEmptyMovieClip(clip_mc._name+"_mask",clip_mc._parent.getNextHighestDepth());
		mask_mc._x = clip_mc._x;
		mask_mc._y = clip_mc._y;

		maskWidth = _maskWidth;
		maskHeight = _maskHeight;

		//Draw square equal to the size of the stage and fill it.
		mask_mc.beginFill(0x000000, 100);
		mask_mc.moveTo(-1,-1);
			mask_mc.lineTo(maskWidth, -1);
			mask_mc.lineTo(maskWidth, maskHeight);
			mask_mc.lineTo(-1, maskHeight);
			mask_mc.lineTo(-1, -1);
		mask_mc.endFill(0xFF00FF, 0);
		clip_mc.setMask(mask_mc);
	}

///////////////////////////////////////////////////////////////////////////////////////////
// End Scroller
//////////////////////////////////////////////////////////////////////////////////////////

Written by Shawn

August 19th, 2009 at 10:42 am

Posted in Uncategorized

One Response to 'Simple Scroller Class AS2'

Subscribe to comments with RSS or TrackBack to 'Simple Scroller Class AS2'.

  1. [...] Click here for more info… [...]

Leave a Reply

Spam Protection by WP-SpamFree