I read a lot of articles after I decided to make my own scrollable DisplayContainer in mobileStyle.
There are a lot of people who had problems with clickable Items they want to scroll and here is my solution after coding a bit around to find a solution. The Class isn't optimized yet but for those who wants to start with a similar project it could help.
Feedback or optimisations are really welcome.
Cheers
package ui.components
{
import flash.display.Sprite;
import flash.display.DisplayObject;
import com.greensock.BlitMask;
import flash.events.Event;
import com.greensock.plugins.TweenPlugin;
import com.greensock.plugins.ThrowPropsPlugin;
import flash.ui.Multitouch;
import flash.ui.MultitouchInputMode;
import flash.events.GestureEvent;
import flash.events.TransformGestureEvent;
import flash.events.MouseEvent;
import fl.transitions.Tween;
import fl.transitions.easing.Elastic;
import fl.transitions.TweenEvent;
public class ScrollableDisplayContainer extends Sprite
{
Multitouch.inputMode = MultitouchInputMode.GESTURE;
TweenPlugin.activate([ThrowPropsPlugin]);
protected const MIN_WIDTH:Number = 20;
protected const MIN_HEIGHT:Number = 20;
private var _width:Number;
private var _height:Number;
private var _mask:BlitMask;
private var _maskHolder:Sprite;
private var _maskContent:DisplayObject;
private var _maskContentHolder:Sprite;
private var hScroll:Boolean = false;
private var vScroll:Boolean = false;
private var scrollBarV:Sprite;
private var scrollBarH:Sprite;
private var enableScrolling:Boolean = false;
private var allowScrolling:Boolean = false;
private var startingPointX:Number;
private var startingPointY:Number;
private var scrollContentPadding:Number = 50;
private var bounceAnimation:Tween;
public function ScrollableDisplayContainer(width:Number, height:Number, content:DisplayObject, name:String = "content")
{
super();
this._width = width;
this._height = height;
this._maskContent = content;
this._maskContent.name = name;
if(!Multitouch.supportsGestureEvents)
{
throw new Error("This Device doesn't support GestureEvents, please go home ... nothing to see here");
}
this.addEventListener(Event.ADDED_TO_STAGE, added);
}
public function changeContent(content:DisplayObject, name:String = "content"):void
{
_maskContentHolder.removeChild(_maskContent);
_maskContent = content;
_maskContentHolder.addChild(_maskContent)
}
private function added(e:Event):void
{
this.removeEventListener(Event.REMOVED, removed);
init();
}
private function removed(e:Event):void
{
this.removeEventListener(Event.REMOVED, removed);
_mask.dispose();
_mask = null;
if (enableScrolling)
{
this.removeEventListener(TransformGestureEvent.GESTURE_SWIPE, scrollFast);
this.removeEventListener(MouseEvent.MOUSE_DOWN, scroll);
allowScrolling = true;
}
}
private function init():void
{
_maskHolder = new Sprite();
this.addChild(_maskHolder);
_maskContentHolder = new Sprite();
_maskHolder.addChild(_maskContentHolder);
_maskContentHolder.addChild(_maskContent);
_mask = new BlitMask(_maskContentHolder);
_mask.x = 0;
_mask.y = 0;
_mask.width = this._width;
_mask.height = this._height;
_mask.bitmapMode = false;
_mask.autoUpdate = true;
setScroller();
}
private function setScroller():void
{
_maskContent.width > _mask.width ? drawScrollBarH() : 0;
_maskContent.height > _mask.height ? drawScrollBarV() : 0;
if (enableScrolling)
{
allowScrolling = true;
this.addEventListener(TransformGestureEvent.GESTURE_SWIPE, scrollFast);
this.addEventListener(MouseEvent.MOUSE_DOWN, scroll);
}
}
private function drawScrollBarH():void
{
enableScrolling = true;
hScroll = true;
var barWidth:Number = _mask.width/(_maskContentHolder.width/_mask.width);
scrollBarH = new Sprite;
scrollBarH.graphics.lineStyle(0, 0x333333);
scrollBarH.graphics.beginFill(0x333333, 1);
scrollBarH.graphics.drawRoundRect(0, 0, barWidth, 4, 3, 3);
scrollBarH.graphics.endFill();
this.addChild(scrollBarH);
scrollBarH.alpha = 0;
scrollBarH.x = 1;
scrollBarH.y = _mask.height - scrollBarH.height-1;
}
private function drawScrollBarV():void
{
enableScrolling = true;
vScroll = true;
var barHeight:Number = _mask.height/(_maskContentHolder.height/_mask.height);
scrollBarV = new Sprite;
scrollBarV.graphics.lineStyle(0, 0x333333);
scrollBarV.graphics.beginFill(0x333333, 1);
scrollBarV.graphics.drawRoundRect(0, 0, 4, barHeight, 3, 3);
scrollBarV.graphics.endFill();
this.addChild(scrollBarV);
scrollBarV.alpha = 0;
scrollBarV.x = _mask.width - scrollBarV.width-1;
scrollBarV.y = 1;
}
private function scrollFast(t:TransformGestureEvent):void
{
t.offsetX == -1 && hScroll && allowScrolling ? scrollFastToLeft() : 0;
t.offsetX == 1 && hScroll && allowScrolling ? scrollFastToRight() : 0;
t.offsetY == 1 && vScroll && allowScrolling ? scrollFastDown() : 0;
t.offsetY == -1 && vScroll && allowScrolling ? scrollFastUp() : 0;
}
private function scrollFastToLeft():void
{
hScroll ? scrollBarH.alpha = 0.3 : 0;
_maskContentHolder.x = Math.max( _maskContentHolder.x - (_maskContentHolder.width/3), (_maskContentHolder.width-_mask.width)*-1 );
if (_maskContentHolder.x < (_maskContentHolder.width-_mask.width)*-1)
{
_maskContentHolder.x = (_maskContentHolder.width-_mask.width)*-1;
}
scrollBarH.x = calcScrollBarXaxis();
checkFinish();
}
private function scrollFastToRight():void
{
hScroll ? scrollBarH.alpha = 0.3 : 0;
_maskContentHolder.x = Math.min(_maskContentHolder.x + (_maskContentHolder.width/3), 0);
if (_maskContentHolder.x > 0)
{
_maskContentHolder.x = 0;
}
scrollBarH.x = calcScrollBarXaxis();
checkFinish();
}
private function scrollFastDown():void
{
vScroll ? scrollBarV.alpha = 0.3 : 0;
_maskContentHolder.y = _maskContentHolder.y + (_maskContentHolder.height/3);
if (_maskContentHolder.y > scrollContentPadding)
{
_maskContentHolder.y = scrollContentPadding;
}
scrollBarV.y = calcScrollBarYaxis();
checkFinish();
}
private function scrollFastUp():void
{
vScroll ? scrollBarV.alpha = 0.3 : 0;
_maskContentHolder.y = _maskContentHolder.y - (_maskContentHolder.height/3);
if (_maskContentHolder.y < (_maskContentHolder.height-_mask.height+scrollContentPadding)*-1)
{
_maskContentHolder.y = (_maskContentHolder.height-_mask.height+scrollContentPadding)*-1;
}
scrollBarV.y = calcScrollBarYaxis();
checkFinish();
}
private function scroll(m:MouseEvent):void
{
if (allowScrolling)
{
allowScrolling = false;
startingPointX = m.stageX;
startingPointY = m.stageY;
this.addEventListener(MouseEvent.MOUSE_UP, endScrolling);
this.addEventListener(MouseEvent.MOUSE_MOVE, scrollToDirection);
}
}
private function endScrolling(m:MouseEvent):void
{
this.removeEventListener(MouseEvent.MOUSE_UP, endScrolling);
this.removeEventListener(MouseEvent.MOUSE_MOVE, scrollToDirection);
checkFinish();
_mask.bitmapMode = false;
}
private function checkFinish():void
{
hScroll ? scrollBarH.alpha = 0 : 0;
vScroll ? scrollBarV.alpha = 0 : 0;
if (_maskContentHolder.y < (_maskContentHolder.height-_mask.height)*-1)
{
startAnimation("y", _maskContentHolder.y, (_maskContentHolder.height-_mask.height)*-1);
}
else if (_maskContentHolder.y > 0)
{
startAnimation("y", _maskContentHolder.y, 0);
}
else
{
allowScrolling = true;
}
}
private function startAnimation(axis:String, start:Number, end:Number):void
{
bounceAnimation = new Tween(_maskContentHolder, axis, Elastic.easeOut, start, end, 1, true);
bounceAnimation.addEventListener(TweenEvent.MOTION_FINISH, animationFinished);
}
private function animationFinished(t:TweenEvent):void
{
bounceAnimation.removeEventListener(TweenEvent.MOTION_FINISH, animationFinished);
allowScrolling = true;
}
private function scrollToDirection(m:MouseEvent):void
{
m.stageX < startingPointX ? scrollLeft(m.stageX, m.stageY) : 0;
m.stageX > startingPointX ? scrollRight(m.stageX, m.stageY) : 0;
m.stageY < startingPointY ? scrollUp(m.stageX, m.stageY) : 0;
m.stageY > startingPointY ? scrollDown(m.stageX, m.stageY) : 0;
}
private function setNewStartingCoordinates(xPos:Number, yPos:Number):void
{
startingPointX = xPos;
startingPointY = yPos;
}
private function calcScrollBarXaxis():Number
{
var cont:Number = (_maskContentHolder.width+_maskContentHolder.x)-_mask.width;
var base:Number = _maskContentHolder.width-_mask.width;
var max:Number = _mask.width - scrollBarH.width;
var act:Number = cont*max/base;
return Math.floor(max - act);
}
private function calcScrollBarYaxis():Number
{
var cont:Number = (_maskContentHolder.height+_maskContentHolder.y)-_mask.height;
var base:Number = _maskContentHolder.height-_mask.height;
var max:Number = _mask.height - scrollBarV.height;
var act:Number = cont*max/base;
var ret:Number = Math.floor(max - act);
ret < 0 ? ret = 0 : 0;
ret > max ? ret = max : 0;
return ret;
}
private function scrollLeft(xPos:Number, yPos:Number):void
{
_mask.bitmapMode = true;
hScroll ? scrollBarH.alpha = 0.3 : 0;
setNewStartingCoordinates(xPos, yPos);
_maskContentHolder.x = _maskContentHolder.x -3;
if (_maskContentHolder.x < (_maskContentHolder.width-_mask.width)*-1)
{
_maskContentHolder.x = (_maskContentHolder.width-_mask.width)*-1;
}
scrollBarH.x = calcScrollBarXaxis();
}
private function scrollRight(xPos:Number, yPos:Number):void
{
_mask.bitmapMode = true;
hScroll ? scrollBarH.alpha = 0.3 : 0;
setNewStartingCoordinates(xPos, yPos);
_maskContentHolder.x = _maskContentHolder.x +3;
if (_maskContentHolder.x > 0)
{
_maskContentHolder.x = 0;
}
scrollBarH.x = calcScrollBarXaxis();
}
private function scrollUp(xPos:Number, yPos:Number):void
{
_mask.bitmapMode = true;
vScroll ? scrollBarV.alpha = 0.3 : 0;
setNewStartingCoordinates(xPos, yPos);
_maskContentHolder.y = _maskContentHolder.y -3;
if (_maskContentHolder.y < (_maskContentHolder.height-_mask.height+scrollContentPadding)*-1)
{
_maskContentHolder.y = (_maskContentHolder.height-_mask.height+scrollContentPadding)*-1;
}
scrollBarV.y = calcScrollBarYaxis();
}
private function scrollDown(xPos:Number, yPos:Number):void
{
_mask.bitmapMode = true;
vScroll ? scrollBarV.alpha = 0.3 : 0;
setNewStartingCoordinates(xPos, yPos);
_maskContentHolder.y = _maskContentHolder.y +3;
if (_maskContentHolder.y > scrollContentPadding)
{
_maskContentHolder.y = scrollContentPadding;
}
scrollBarV.y = calcScrollBarYaxis();
}
}
}
Usage:
import flash.display.Sprite;
import ui.components.ScrollableDisplayContainer;
import flash.events.MouseEvent;
var a:Sprite = new Sprite();
a.graphics.lineStyle(0, 0xFF0000);
a.graphics.beginFill(0xFF0000, 1);
a.graphics.drawRect(0, 0, 1000, 800);
a.graphics.endFill();
var aa:Sprite = new Sprite();
aa.graphics.lineStyle(0, 0xFF0000);
aa.graphics.beginFill(0x00FF00, 1);
aa.graphics.drawRect(0, 0, 1000, 500);
aa.graphics.endFill();
aa.buttonMode=true;
aa.addEventListener(MouseEvent.CLICK, clicked);
function clicked(m:MouseEvent):void
{
trace ('clicked: '+m.target);
}
var c:Sprite = new Sprite();
c.addChild(a);
a.x = 0;
a.y = 0;
c.addChild(aa);
aa.x = 0;
aa.y = 200;
var b:ScrollableDisplayContainer;
b = new ScrollableDisplayContainer(600, 560, c);
addChild(;