Skip to content

Instantly share code, notes, and snippets.

@serjek
Created April 4, 2016 17:58
Show Gist options
  • Save serjek/c31cd8cb12e305a1895a5c04675aeeec to your computer and use it in GitHub Desktop.
Save serjek/c31cd8cb12e305a1895a5c04675aeeec to your computer and use it in GitHub Desktop.
package feathers.controls
{
import flash.geom.Point;
import flash.utils.getTimer;
import feathers.controls.StackScreenNavigator;
import feathers.controls.supportClasses.IScreenNavigatorItem;
import feathers.events.ExclusiveTouch;
import feathers.events.FeathersEventType;
import feathers.system.DeviceCapabilities;
import feathers.utils.math.roundToNearest;
import starling.animation.Transitions;
import starling.animation.Tween;
import starling.core.Starling;
import starling.display.DisplayObject;
import starling.events.Event;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
import starling.filters.DropShadowFilter;
public class SwipeStackScreenNavigator extends StackScreenNavigator
{
public var swipeEnabled:Boolean;
protected var _openGestureEdgeSize:Number = 0.4;
protected var _minimumDrawerThrowVelocity:Number = 5;
protected var _minimumDragDistance:Number = 0.4;
protected var _openOrCloseEase:Object = Transitions.EASE_OUT;
protected var _openOrCloseDuration:Number = 0.25;
//
protected var _openOrCloseTween:Tween;
private static const HELPER_POINT:Point = new Point();
private static const CURRENT_VELOCITY_WEIGHT:Number = 2.33;
private static const VELOCITY_WEIGHTS:Vector.<Number> = new <Number>[1, 1.33, 1.66, 2];
private static const MAXIMUM_SAVED_VELOCITY_COUNT:int = 4;
//
//
protected var pendingToggleDuration:Number;
protected var touchPointID:int = -1;
protected var _isDragging:Boolean = false;
protected var _startTouchX:Number;
protected var _startTouchY:Number;
protected var _currentTouchX:Number;
protected var _currentTouchY:Number;
protected var _previousTouchTime:int;
protected var _previousTouchX:Number;
protected var _previousTouchY:Number;
protected var _velocityX:Number = 0;
protected var _velocityY:Number = 0;
protected var _previousVelocityX:Vector.<Number> = new <Number>[];
protected var _previousVelocityY:Vector.<Number> = new <Number>[];
private var _isTempScreenOpen:Boolean;
private var tempScreen:DisplayObject;
private var tempScreenID:String;
protected var _tempScreenOffset:int;
public function SwipeStackScreenNavigator()
{
super();
this.addEventListener(TouchEvent.TOUCH, navigator_touchHandler);
};
///////////
protected function navigator_touchHandler(event:TouchEvent):void
{
if(!this._isEnabled || this._openOrCloseTween)
{
this.touchPointID = -1;
return;
};
//
if(this.touchPointID >= 0)
{
var touch:Touch = event.getTouch(this, null, this.touchPointID);
if(!touch)
{
return;
}
if(touch.phase == TouchPhase.MOVED)
{
this.handleTouchMoved(touch);
if(!this._isDragging)
{
this.checkForDragToOpen();
}
if(this._isDragging)
{
this.handleDragMove();
}
}
else if(touch.phase == TouchPhase.ENDED)
{
this.touchPointID = -1;
if(this._isDragging)
{
this.handleDragEnd();
this.dispatchEventWith(FeathersEventType.END_INTERACTION);
} else {
this.handleTouchEnd();
};
};
}
else
{
touch = event.getTouch(this, TouchPhase.BEGAN);
if(!touch)
{
return;
}
this.handleTouchBegan(touch);
}
};
////////
protected function handleTouchBegan(touch:Touch):void
{
var exclusiveTouch:ExclusiveTouch = ExclusiveTouch.forStage(this.stage);
/*
if(exclusiveTouch.getClaim(touch.id))
{
//already claimed
return;
}*/
touch.getLocation(this, HELPER_POINT);
var localX:Number = HELPER_POINT.x;
var localY:Number = HELPER_POINT.y;
if(!this.swipeEnabled || this.stackCount<=1)
{
return;
};
var isNearLeftEdge:Boolean = false;
var leftInches:Number = localX / (DeviceCapabilities.dpi / Starling.contentScaleFactor);
if(leftInches >= 0 && leftInches <= this._openGestureEdgeSize)
{
isNearLeftEdge = true;
};
if(!isNearLeftEdge)
{
return;
};
//
this.touchPointID = touch.id;
this._velocityX = 0;
this._velocityY = 0;
this._previousVelocityX.length = 0;
this._previousVelocityY.length = 0;
this._previousTouchTime = getTimer();
this._previousTouchX = this._startTouchX = this._currentTouchX = localX;
this._previousTouchY = this._startTouchY = this._currentTouchY = localY;
this._isDragging = false;
this._isTempScreenOpen=false;
this._tempScreenOffset = this.width*.25;
//
exclusiveTouch.addEventListener(Event.CHANGE, exclusiveTouch_changeHandler);
};
private function handleTouchEnd():void {
this.cleanTempScreen();
};
/**
* @private
*/
protected function handleTouchMoved(touch:Touch):void
{
touch.getLocation(this, HELPER_POINT);
this._currentTouchX = HELPER_POINT.x;
this._currentTouchY = HELPER_POINT.y;
var now:int = getTimer();
var timeOffset:int = now - this._previousTouchTime;
if(timeOffset > 0)
{
//we're keeping previous velocity updates to improve accuracy
this._previousVelocityX[this._previousVelocityX.length] = this._velocityX;
if(this._previousVelocityX.length > MAXIMUM_SAVED_VELOCITY_COUNT)
{
this._previousVelocityX.shift();
}
this._previousVelocityY[this._previousVelocityY.length] = this._velocityY;
if(this._previousVelocityY.length > MAXIMUM_SAVED_VELOCITY_COUNT)
{
this._previousVelocityY.shift();
}
this._velocityX = (this._currentTouchX - this._previousTouchX) / timeOffset;
this._velocityY = (this._currentTouchY - this._previousTouchY) / timeOffset;
this._previousTouchTime = now;
this._previousTouchX = this._currentTouchX;
this._previousTouchY = this._currentTouchY;
}
}
/**
* @private
*/
protected function handleDragEnd():void
{
//take the average for more accuracy
var sum:Number = this._velocityX * CURRENT_VELOCITY_WEIGHT;
var velocityCount:int = this._previousVelocityX.length;
var totalWeight:Number = CURRENT_VELOCITY_WEIGHT;
for(var i:int = 0; i < velocityCount; i++)
{
var weight:Number = VELOCITY_WEIGHTS[i];
sum += this._previousVelocityX.shift() * weight;
totalWeight += weight;
}
var inchesPerSecondX:Number = 1000 * (sum / totalWeight) / (DeviceCapabilities.dpi / Starling.contentScaleFactor);
sum = this._velocityY * CURRENT_VELOCITY_WEIGHT;
velocityCount = this._previousVelocityY.length;
totalWeight = CURRENT_VELOCITY_WEIGHT;
for(i = 0; i < velocityCount; i++)
{
weight = VELOCITY_WEIGHTS[i];
sum += this._previousVelocityY.shift() * weight;
totalWeight += weight;
}
var inchesPerSecondY:Number = 1000 * (sum / totalWeight) / (DeviceCapabilities.dpi / Starling.contentScaleFactor);
this._isDragging = false;
//
if(inchesPerSecondX > this._minimumDrawerThrowVelocity)
{
this._isTempScreenOpen = true;
}
else if(inchesPerSecondX < -this._minimumDrawerThrowVelocity)
{
this._isTempScreenOpen = false;
}
else
{
this._isTempScreenOpen = roundToNearest(this.activeScreen.x, this.activeScreen.width) != 0;
};
//
this.openOrCloseTempScreen();
}
/**
* @private
*/
protected function handleDragMove():void
{
//setup screens if required
if (!this.tempScreen) {
this.tempScreenID=this._stack[this._stack.length-1].id;
var item:IScreenNavigatorItem = IScreenNavigatorItem(this._screens[this.tempScreenID]);
this.tempScreen = item.getScreen();
this.tempScreen.width=this.width;
this.tempScreen.height=this.height;
this.addChildAt(this.tempScreen,0);
//
var f:DropShadowFilter=new DropShadowFilter(5,Math.PI,0x000000,.5,10);
this.activeScreen.filter=f;
};
//
var contentX:Number = 0;
var contentY:Number = 0;
//
var tempScreenWidth:Number = this.tempScreen.width;
if(this._isTempScreenOpen)
{
contentX = tempScreenWidth + this._currentTouchX - this._startTouchX;
}
else
{
contentX = this._currentTouchX - this._startTouchX;
}
if(contentX < 0)
{
contentX = 0;
}
if(contentX > tempScreenWidth)
{
contentX = tempScreenWidth;
};
//
var tempScrFraction:Number = -(1-(contentX/this.tempScreen.width))*this._tempScreenOffset;
this.tempScreen.x = tempScrFraction;
this.activeScreen.x = contentX;
//
this.openOrCloseTween_onUpdate();
};
protected function checkForDragToOpen():void
{
var horizontalInchesMoved:Number = (this._currentTouchX - this._startTouchX) / (DeviceCapabilities.dpi / Starling.contentScaleFactor);
var verticalInchesMoved:Number = (this._currentTouchY - this._startTouchY) / (DeviceCapabilities.dpi / Starling.contentScaleFactor);
if(horizontalInchesMoved >= this._minimumDragDistance) {
this._isDragging = true;
};
//
if(this._isDragging)
{
this._startTouchY = this._currentTouchY;
var exclusiveTouch:ExclusiveTouch = ExclusiveTouch.forStage(this.stage);
exclusiveTouch.claimTouch(this.touchPointID, this);
exclusiveTouch.removeEventListener(Event.CHANGE, exclusiveTouch_changeHandler);
//
this.dispatchEventWith(FeathersEventType.BEGIN_INTERACTION);
}
};
protected function exclusiveTouch_changeHandler(event:Event, touchID:int):void
{
if(this.touchPointID < 0 || this.touchPointID != touchID || this._isDragging)
{
return;
}
var exclusiveTouch:ExclusiveTouch = ExclusiveTouch.forStage(this.stage);
if(exclusiveTouch.getClaim(touchID) == this)
{
return;
}
this.touchPointID = -1;
};
protected function openOrCloseTempScreen():void
{
if(!this.tempScreen)
{
return;
}
if(this._openOrCloseTween)
{
this._openOrCloseTween.advanceTime(this._openOrCloseTween.totalTime);
Starling.juggler.remove(this._openOrCloseTween);
this._openOrCloseTween = null;
};
//
var targetPosition:Number = this._isTempScreenOpen ? this.activeScreen.width : 0;
var blurOffset:int = this._isTempScreenOpen ? 20: 0;
var duration:Number = this.pendingToggleDuration;
if(duration !== duration) //isNaN
{
duration = this._openOrCloseDuration;
}
this.pendingToggleDuration = NaN;
this._openOrCloseTween = new Tween(this.activeScreen, duration, this._openOrCloseEase);
this._openOrCloseTween.animate("x", targetPosition+blurOffset);
this._openOrCloseTween.onUpdate = openOrCloseTween_onUpdate;
this._openOrCloseTween.onComplete = openOrCloseTween_onComplete;
Starling.juggler.add(this._openOrCloseTween);
};
protected function openOrCloseTween_onUpdate():void
{
if (!this._openOrCloseTween)
return;
var p:Number=Math.min(1,this.activeScreen.x/this.activeScreen.width);
var tempScrFraction:Number = -(1-p)*this._tempScreenOffset;
this.tempScreen.x=tempScrFraction;
//
};
protected function openOrCloseTween_onComplete():void
{
this.cleanTempScreen();
//
if(this._isTempScreenOpen)
{
this.showScreenInternal(this.tempScreenID, null);
this._stack.pop();
this.dispatchEventWith(FeathersEventType.TRANSITION_COMPLETE);
}
else
{
this.dispatchEventWith(FeathersEventType.TRANSITION_CANCEL);
//this.dispatchEventWith(Event.CLOSE, false, this.tempScreen);
}
};
//
private function cleanTempScreen():void {
this._openOrCloseTween = null;
if (this.tempScreen) {
this.tempScreen.removeFromParent(true);
this.tempScreen=null;
};
};
///////////
//set screen to stack at specified index
public function setScreenToIndex(p_scr:String,p_ind:int):void {
if (p_ind>=this._stack.length||p_ind<0)
return;
this._stack[p_ind].id=p_scr;
};
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment