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:

ListScroll.swf

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:

ListScroll2.swf

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

19 Comments

  1. 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

  2. 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.

  3. 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

    1. 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

  4. 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

  5. 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

      1. 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

  6. 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.

    1. 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.

  7. Hey Michael, nicely done! You always do clean great code. Miss working with you. Only a couple of things you might want to try, use only one listener for the mouse events on the parent list object, makes for less overhead if you have a long list. Could also have used an event.updateAfterEvent () in your handleEnterFrame, that could help smooth out the animation a bit. I would put it in the conditional so it only fires when your actually going to scroll.

  8. Hello Mr. Michael:

    That´s a great tutorial, thanks for sharing, i have an list scroll, some different, but i would like to ask something, i hope you can help me, the scrollList.swf is loaded into a main, so when is scrolled the list moves around the stage, is there any way to hide the top?

    Thanks in advance for any help.

    Regards.

  9. Hello Mr. Michael: thanks for sharing your knowlege in this tutorial, i have a question please help me i have a scrollList file which is loaded into a main swf, but the list moves around the stage, is there any way to hide the top?
    Thanks for any help in advance.
    Regards
    Josy

  10. Thank you for your great code!!

    I managed to create 2 scroll boxes on my stage with separate label arrays by adding an additional parameter, which then give me the label string as a value when selected.

    The problem is I can’t access the values on the stage to work with my two values.
    Nor can one scroll menu see the other and know what’s been selected.

    As a time line coder, any reference on getters/setters, StageReference is simply beyond me.

    Appreciate any (more!) help.

    Thank you in advance.
    stevevg

Comments are closed.