AS3, CS5, ENTER_FRAME, Event, Flash, List, MouseEvent, MOUSE_MOVE, TweenLite
Simple AS3 Mouse Scrolling List
Here is a simple ActionScript 3 list that scrolls with selectable items done two ways. The list itself acts like a component added to the stage, it can be sized and placed anywhere on the stage. The first way scrolling is achieved is based on the movement of the mouse over a defined masked area.
The smooth movement of the list is accomplished with TweenLite:
The second way to scroll the list was on an enter frame event. This method is not as smooth because TweenLite could not be produced to create the movement of the list:
That’s pretty much the extent of it. Below is the code for the list application and scrolling list.
List.as
package
{
import flash.display.MovieClip;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
[SWF( width = '320', height = '440', backgroundColor = '#000000', frameRate = '30')]
public class ListScroll extends Sprite
{
private var list:MouseScrollList;
public function ListScroll()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
list = new MouseScrollList(300, 400, false);
list.y = 20;
list.x = 5;
this.addChild(list);
}
}
}
MouseScrollList.as
package
{
import com.greensock.TweenLite;
import com.greensock.easing.Quad;
import flash.display.MovieClip;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.text.TextField;
public class MouseScrollList extends Sprite
{
private var background:Sprite;
private var menuMask:Shape;
private var listHitArea:Shape;
private var list:Sprite;
private var prevY:Number = 0;
private var listHeight:Number;
private var hitAreaHeight:Number;
private var listY:Number;
private var listX:Number;
private var listInitY:Number;
private var verticalPadding:Number = 5;
private var itemHeight:Number = 35;
private var children:Number = 50;
private var componentWidth:Number;
private var componentHeight:Number;
private var scrollOnMouseMove:Boolean = false; // uses mouse move instead of enter frame, but doesn't work as good as I thought
private var scroll:Boolean = true; // on mouse down set to false to hold scroll position in list when item is clicked
public function MouseScrollList(w:Number, h:Number, scrollOnMouseMove:Boolean = false)
{
this.componentWidth = w;
this.componentHeight = h;
this.scrollOnMouseMove = scrollOnMouseMove;
addEventListener(Event.ADDED_TO_STAGE, init);
addEventListener(Event.REMOVED, destroy);
}
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
if(scrollOnMouseMove){
addEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
} else {
addEventListener(Event.ENTER_FRAME, handleEnterFrame);
}
listHitArea = new Shape
menuMask = new Shape();
menuMask.graphics.clear();
menuMask.graphics.beginFill(0xFFFFFF,.4);
menuMask.graphics.drawRect(0, 0, componentWidth, componentHeight)
menuMask.graphics.endFill();
this.addChild(menuMask);
list = new Sprite();
list.mask = menuMask;
this.addChild(list);
for(var i:int = 0; i < children; i++) {
var textField:TextField = new TextField();
textField.text = String(i);
textField.mouseEnabled = false;
var item:Sprite = new Sprite();
item.graphics.clear();
item.graphics.beginFill(0xFF0000, 1);
item.graphics.drawRect(0, 0, componentWidth - verticalPadding*2, itemHeight)
item.graphics.endFill()
item.y = i*(verticalPadding + itemHeight);
item.x = verticalPadding;
item.addEventListener(MouseEvent.ROLL_OVER, handleRollOver);
item.addEventListener(MouseEvent.ROLL_OUT, handleRollOut);
item.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
item.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
item.addChild(textField)
list.addChild(item);
}
listHitArea = new Shape();
listHitArea.graphics.clear();
listHitArea.graphics.beginFill(0xFFFFFF,0);
listHitArea.graphics.drawRect(0, 0, componentWidth, componentHeight)
listHitArea.graphics.endFill();
this.addChild(listHitArea);
var point:Point = new Point(listHitArea.x, listHitArea.y);
point = this.globalToLocal(point);
listX = point.x;
listY = point.y;
listInitY = list.y;
hitAreaHeight = listHitArea.height;
listHeight = children*(verticalPadding + itemHeight) - hitAreaHeight;
}
private function scrollMouseMove(y:Number):void
{
var percent:Number = y/hitAreaHeight;
var newY:Number = -(Math.round(listHeight*percent));
if(newY + itemHeight*3 > hitAreaHeight) {
newY = hitAreaHeight;
} else if (newY + itemHeight*3 > listInitY) {
newY = listInitY;
}
TweenLite.to(list, 2, {y:newY, ease:Quad.easeOut});
}
private function scrollEnterFrame(y:Number):void
{
var distance:Number = Math.cos( ( -(y + listY)/hitAreaHeight)*Math.PI )*15;
var currentY:Number = list.y;
var newY:Number;
if( (currentY + distance - verticalPadding) > listInitY + verticalPadding) return;
newY = list.y + distance;
var delta:Number = Math.abs(prevY - newY);
if(delta < 1) {
list.y = prevY;
return;
}
if(newY >= listInitY) {
newY = listInitY;
} else if (Math.abs(newY) > listHeight){
newY = -listHeight;
}
prevY = list.y;
list.y = newY;
}
private function handleMouseMove(event:MouseEvent):void
{
var x:Number = this.mouseX + Math.abs(listX);
var y:Number = this.mouseY + Math.abs(listY);
if( menuMask.hitTestPoint( x, y ) && scroll) {
scrollMouseMove(y - Math.abs(listY));
}
}
private function handleEnterFrame(event:Event):void
{
var x:Number = this.mouseX + Math.abs(listX);
var y:Number = this.mouseY + Math.abs(listY);
if( listHitArea.hitTestPoint( x, y ) && scroll ) {
scrollEnterFrame(y);
}
}
public function handleRollOver(e:MouseEvent):void
{
e.target.alpha = .5;
}
private function handleRollOut(e:MouseEvent):void
{
e.target.alpha = 1;
}
private function handleMouseDown(e:MouseEvent):void
{
e.target.alpha = .2;
scroll = false;
}
private function handleMouseUp(e:MouseEvent):void
{
e.target.alpha = 1;
scroll = true;
}
private function destroy(e:Event):void
{
removeEventListener(Event.REMOVED, destroy);
var list:MovieClip = list as MovieClip;
if(list.numChildren != 0) {
var i:int = list.numChildren;
while( i-- ){
var item:Sprite = list.getChildAt(i) as Sprite;
item.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
item.removeEventListener(MouseEvent.MOUSE_UP, handleMouseDown);
item.removeEventListener(MouseEvent.ROLL_OVER, handleRollOver);
item.removeEventListener(MouseEvent.ROLL_OUT, handleRollOut);
list.removeChildAt( i );
}
}
removeEventListener(Event.ADDED_TO_STAGE, init);
removeEventListener(Event.REMOVED, destroy);
removeEventListener(Event.ENTER_FRAME, handleEnterFrame);
removeEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
}
}
}
You can download a Flash working example here.
- Mister
Thank you very much. I’ve been looking for something like this for 3 weeks. Great work.
Could you offer any tips on plugging this functionality into a List component that calls a mySQL database as it’s dataProvider?
Thanks a million,
pk
Very nice list… Any Idea on how to get the text values out of each list item. I want to populate this using xml and would like to access the text values of each on the mouse down function. I tried e.target.textField.text and e.currentTarget.textField.text. no luck…
Thanks for the help in advance.
Hi and thanks for a GREAT peace of work. It seems that a person is good enough to write such code and share it and unfortunately has to answer so many querruis but as a newby to AS3 I can understand that people will ask.
From my perspective, how would I implement this in practice ? I know I need an .fla file which will probably reference the List.as file but how does the .fla referfence the MouseScrollList.as file ?
This would be obvious to seasoned AS3 persons.
Thanks
Paul
That is a tough question to answer for someone new to AS3 programming. You basically, without going into too much detail, create an ActionScript class that is associated with your Flash in the ActionScript Settings. I created a new Fla named ScrollingListExample.fla and an associated document class called ScrollingListExample.as. Save your Fla and the document class into the same directory. Make a new ActionScript file from the Flash IDE called MouseScrollList.as and copy/paste the MouseScrollList.as code into that file from this post. Within your ScrollingListExample.as file you create a new instance of the MouseScrollList.as file and add it to the display. You can do other things as well, such as set it’s x, y coordinates or width, height. Here is an example of the document class:
package { import flash.display.MovieClip; import flash.events.Event; public class ScrollingListExample extends MovieClip { var mouseScrollList:MouseScrollList; public function ScrollingListExample() { addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, init); mouseScrollList = new MouseScrollList(300, 400, false); mouseScrollList.y = 0; mouseScrollList.x = 0; this.addChild(mouseScrollList); } } }Don’t forget that you also need to download the Tweenlite code and put the that in the same directory as your Fla and other classes.
-Mister
@Paul
I placed a link at the bottom of the post with a working example of a Flash project using the list. Hope this helps!
Thank You SO MUCH for your kindness.
Now not only do I have a working example but I can also study how all the files are linked together in the AS3 way. Yes coming from AS2 the new way is a little daunting but I am a bit more confident now. HGowever very keen to see how we link 2 .as files to the main .fla.
Thanks again.
Paul
This may be a stupid question but in line 75 of MouseScrollList.as it says;
textField.text = String(i);
Now if I change this to;
textField.text = “Hello”;
Then every position in the scroll list is labelled “hello” so obviously this is where we feed in the labels we want.
No my question is – How do we feed our required labels into String(i) ?
I would think that if I define an array named “String” and bring it onboard then line 75, as currently written, would label each item in the scroll as I require.
Also where do we detect the index of the item selected by the user as we will need this as it will define any subsequent actions from the list ?
This is obviously handled in the handleMouseDown event as putting a trace in there as shown below, traces out exactly as I select with mouse down. Now all I need to know is “what is the index of the button I clicked on”
private function handleMouseDown(e:MouseEvent):void
{
e.target.alpha = .2;
scroll = false;
trace(“we have selected”);
}
Thanks
Paul
You may have misunderstood the purpose of this example, it’s to show you how to scroll the list. This is not a complete list, it doesn’t have all the functionality. I did make something you may be able to either modify or copy/paste from to make a complete list. Check out the AS3 Android/iOS list I made: http://thanksmister.com/2010/10/14/android-as3-scrolling-list/
OK.
Thanks – I have that one and its also VERY good and I have figured out how to include it in my current project.
Thanks a million.
Paul
It would be nice if your simple example didn’t require the very latest version of flash. I have cs4 and the example fla is useless to me.
AS3 code is AS3, I am pretty sure that you could manage to get the code running even if you have CS4. Maybe not the .fla, but that is no big deal right, you just hook it up to a new .fla in your CS4…. oh and give me a break about the complaining, that is pretty useless as well.