Instantly share code, notes, and snippets.
Created
April 4, 2016 17:58
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save serjek/c31cd8cb12e305a1895a5c04675aeeec to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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