Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
'New files added: 8, Files modified: 4, Files deleted: 12'
'Added 8 files: animator/ easing/Easing.swift events/ movers/beta/ movers/rubberband/ movers/utils/DEPRECATEDFrameTicker.swift physics/ proxy/ Deleted 12 files: AnimEvent.swift Animatable.swift Animation.swift Animator.swift BaseAnimation.swift Easing.swift LoopingAnimator.swift movers/SnapFriction.swift movers/physics/Easer.swift movers/physics/PhysicsAnimationKind.swift movers/physics/Springer.swift movers/utils/FrameTicker.swift Modified 4 files: README.md movers/Friction.swift movers/Mover.swift movers/RubberBand.swift '
- Loading branch information
Showing
27 changed files
with
390 additions
and
142 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import Cocoa | ||
/** | ||
* This class animates something from A to B with a easing curve attached | ||
* NOTE: This animation class is more like stock animation, less interuptable than "physics based animation" | ||
* TODO: ⚠️️ Take a look at other animation libs 👈 | ||
* TODO: ⚠️️ Add onComplete selector callback method on init and as a variable, do the same with method, use optional to assert if they exist or not | ||
* TODO: ⚠️️ Seek,reverse,repeate,autoRepeat | ||
*/ | ||
class Animator:FrameAnimator { | ||
var frameTick:FrameTick/*The closure method that is called on every "frame-tick" and that changes the property, you can use a var closure or a regular method, probably even an inline closure*/ | ||
var currentFrameCount:CGFloat = 0/*curFrameCount, this is need in order to know when the animation is complete*/ | ||
var easing:EasingEquation/*Variable for holding the easing method*/ | ||
var initValues:InitValues/*Stores the intial config values for the animation, duration,fromValue, toValue*/ | ||
init(onFrame:@escaping FrameTick = {_ in}, initValues:InitValues = Animator.initValues, easing:@escaping EasingEquation = Easing.linear.ease){ | ||
self.frameTick = onFrame | ||
self.initValues = initValues | ||
self.easing = easing | ||
super.init(AnimProxy.shared) | ||
} | ||
/** | ||
* Fires on every frame tick | ||
*/ | ||
override func onFrame(){ | ||
let val:CGFloat = easing(currentFrameCount, from, to-from, framesToEnd) | ||
frameTick(val)/*Call the callBack onFrame method*/ | ||
if(currentFrameCount == framesToEnd){ | ||
stop()/*Stop the animation*/ | ||
super.onEvent(AnimEvent(AnimEvent.completed,self))/*Notify listeners that the animation completed*/ | ||
} | ||
self.currentFrameCount += 1 | ||
} | ||
/*DEPRECATED*/ | ||
init(_ animatable:AnimProxyKind, _ duration:CGFloat = 0.5, _ from:CGFloat, _ to:CGFloat, _ callBack:@escaping FrameTick, _ easing:@escaping EasingEquation = Linear.ease){ | ||
initValues = (duration:duration,from:from,to:to) | ||
self.frameTick = callBack | ||
self.easing = easing | ||
super.init(animatable) | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import Foundation | ||
|
||
/** | ||
* NOTE: remember this needs to support many different animators and also simultan animations, so it cant be too intertwined | ||
* TODO: make FrameAnimator2 that does not extend EventSender | ||
* TODO: LoopAnimator2 | ||
* TODO: if you need to stop the entire anim chain you need to store each successive anim in an array and stop the one that is running, you can create utilitity methods that does this for you | ||
* TODO: Later you can maybe create a class that is called AnimSeq, which can sequence anim from a json file, akin to your legacy project | ||
* TODO: API like: spring(view, delay: 0.5, spring: 800, friction: 10, mass: 10) {} | ||
* TODO: API like: animate(view, duration: 1, curve: .bezier(1, 0.4, 1, 0.5)) {$0.x = finalValue} | ||
*/ | ||
class Animator2:FrameAnimator2 { | ||
var frameTick:FrameTick | ||
var currentFrameCount:CGFloat = 0/*curFrameCount, this is needed in order to know when the animation is complete*/ | ||
var initValues:InitValues | ||
var easing:EasingEquation/*Variable for holding the easing method*/ | ||
typealias Completed = () -> Void | ||
// | ||
var completed:Completed = {} | ||
//(CGFloat) -> Animator2 /*Makes the return type less verbose*/ | ||
init(initValues:Animator2.InitValues = Animator2.initValues, easing:@escaping EasingEquation = Easing.linear.ease, closure: @escaping FrameTick = {_ in}) { | ||
self.initValues = initValues | ||
self.frameTick = closure | ||
self.easing = easing | ||
//return TestingClass() | ||
super.init(AnimProxy2.shared) | ||
} | ||
/** | ||
* Fires on every frame tick | ||
*/ | ||
override func onFrame(){ | ||
let val:CGFloat = easing(currentFrameCount, from, to-from, framesToEnd) | ||
frameTick(val)/*Call the callBack onFrame method*/ | ||
if(currentFrameCount == framesToEnd){ | ||
stop()/*Stop the animation*/ | ||
//_ = completed(Animator.initValues, {_ in})//the animation completed, call the completed closure | ||
completed() | ||
} | ||
self.currentFrameCount += 1 | ||
} | ||
/** | ||
* NOTE: we need onComplete in addition to complete because complete can't return self, so chaining won't work | ||
*/ | ||
func onComplete(closure: @escaping Completed) -> Self{ | ||
completed = closure//assign the closure | ||
return self | ||
} | ||
} | ||
|
||
extension Animator2 { | ||
typealias InitValues = (dur:CGFloat,from:CGFloat,to:CGFloat)/*Signature for initValues*/ | ||
static var initValues:InitValues = (dur:0.5,from:0,to:1)/*Default init values*/ | ||
static var fps:CGFloat = 60//<--TODO: ⚠️️ this should be derived from a device variable | ||
var duration:CGFloat {get{return initValues.dur}set{initValues.dur = newValue}}/*In seconds*/ | ||
var from:CGFloat {get{return initValues.from}set{initValues.from = newValue}}/*From this value*/ | ||
var to:CGFloat {get{return initValues.to}set{initValues.to = newValue}}/*To this value*/ | ||
var framesToEnd:CGFloat {return Animator.fps * duration}/*totFrameCount*/ | ||
} | ||
//extension Animator2{ | ||
// struct InitValues2{ | ||
// var duration:CGFloat | ||
// var from:CGFloat | ||
// var to:CGFloat | ||
// } | ||
//} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import Foundation | ||
|
||
typealias FrameTick = (CGFloat)->Void/*the callBack signature for onFrame ticks*/ | ||
extension Animator { | ||
typealias InitValues = (duration:CGFloat,from:CGFloat,to:CGFloat)/*Signature for initValues*/ | ||
static var initValues:InitValues = (duration:0.5,from:0,to:1)/*Default init values*/ | ||
static var fps:CGFloat = 60//<--TODO: ⚠️️ this should be derived from a device variable | ||
var duration:CGFloat {get{return initValues.duration}set{initValues.duration = newValue}}/*In seconds*/ | ||
var from:CGFloat {get{return initValues.from}set{initValues.from = newValue}}/*From this value*/ | ||
var to:CGFloat {get{return initValues.to}set{initValues.to = newValue}}/*To this value*/ | ||
var framesToEnd:CGFloat {return Animator.fps * duration}/*totFrameCount*/ | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import Cocoa | ||
/** | ||
* FrameAnimator serves as the Core Animator in this Animation library | ||
* TODO: ⚠️️ Consider not using EventSender in the animation lib and instead setup callbacks so that it can work standalone, also callbacks works better when setting up chaining | ||
* NOTE: We use EventSender for in-frequent events such as onComplete or onStop and we use a regular callback method as its very frequent | ||
*/ | ||
class FrameAnimator:EventSender {//rename to FrameAnimator | ||
var animProxy:AnimProxyKind/*Reference to where the displayLink resides*/ | ||
init(_ animatable:AnimProxyKind = AnimProxy.shared){ | ||
self.animProxy = animatable | ||
} | ||
/** | ||
* This is called from the AnimProxy.onFrameOnMainThread method | ||
*/ | ||
func onFrame(){ | ||
fatalError("Must be overwritten in subclass") | ||
} | ||
/** | ||
* Start the animation | ||
*/ | ||
func start(){ | ||
animProxy.animators.append(self)/*Add your self to the list of animators that gets the onFrame call*/ | ||
if(!CVDisplayLinkIsRunning(animProxy.displayLink)){CVDisplayLinkStart(animProxy.displayLink)}/*start the displayLink if it isn't already running*/ | ||
} | ||
/** | ||
* Stop the animation | ||
*/ | ||
func stop(){ | ||
animProxy.animators.removeAt(animProxy.animators.indexOf(self))/*If none exist -1 is returned and none is removed*/ | ||
if(animProxy.animators.isEmpty && CVDisplayLinkIsRunning(animProxy.displayLink)){CVDisplayLinkStop(animProxy.displayLink)}/*stops the frame ticker if there is no active running animators*/ | ||
super.onEvent(AnimEvent(AnimEvent.stopped,self))/*Notify listners the animation has stopped*/ | ||
} | ||
} | ||
extension FrameAnimator { | ||
/** | ||
* Assert if an animator is active or not, you can also check if the Animator is nil to check if is active or not | ||
* TODO: ⚠️️ Name this hasStopped or isActive | ||
*/ | ||
var stopped:Bool {return animProxy.animators.indexOf(self) == -1} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import Cocoa | ||
|
||
/** | ||
* FrameAnimator serves as the Core Animator in this Animation library | ||
* TODO: ⚠️️ Consider not using EventSender in the animation lib and instead setup callbacks so that it can work standalone, also callbacks works better when setting up chaining | ||
* NOTE: We use EventSender for in-frequent events such as onComplete or onStop and we use a regular callback method as its very frequent | ||
*/ | ||
class FrameAnimator2 {/*Rename to FrameAnimator*/ | ||
var animProxy:AnimProxyKind2/*Reference to where the displayLink resides*/ | ||
init(_ animatable:AnimProxyKind2 = AnimProxy2.shared){ | ||
self.animProxy = animatable | ||
} | ||
/** | ||
* This is called from the AnimProxy.onFrameOnMainThread method | ||
*/ | ||
func onFrame(){ | ||
fatalError("Must be overwritten in subclass") | ||
} | ||
/** | ||
* Start the animation | ||
*/ | ||
func start(){ | ||
animProxy.animators.append(self)/*Add your self to the list of animators that gets the onFrame call*/ | ||
if(!CVDisplayLinkIsRunning(animProxy.displayLink)){CVDisplayLinkStart(animProxy.displayLink)}/*start the displayLink if it isn't already running*/ | ||
} | ||
/** | ||
* Stop the animation | ||
*/ | ||
func stop(){ | ||
animProxy.animators.removeAt(animProxy.animators.indexOf(self))/*If none exist -1 is returned and none is removed*/ | ||
if(animProxy.animators.isEmpty && CVDisplayLinkIsRunning(animProxy.displayLink)){CVDisplayLinkStop(animProxy.displayLink)}/*stops the frame ticker if there is no active running animators*/ | ||
} | ||
} | ||
extension FrameAnimator2 { | ||
/** | ||
* Assert if an animator is active or not, you can also check if the Animator is nil to check if is active or not | ||
* TODO: ⚠️️ Name this hasStopped or isActive | ||
*/ | ||
var stopped:Bool {return animProxy.animators.indexOf(self) == -1} | ||
} | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import Foundation | ||
/** | ||
* Makes it possible to create looping animations, n-loops or infinite-loops | ||
* NOTE: use stop() to stop the animation if the animation is infinite, with n-loops the animation stops when the last repeat has run | ||
* NOTE: we don't use structs as init values because structs look like this: SomeStruct(from:0...) bbut tupples + typealias looks simpler (from:0...) more like method args would | ||
* PARAM: duration: in seconds | ||
* PARAM: callBack: is the callback ref that is called on every "frame tick" | ||
*/ | ||
class LoopAnimator2:Animator2{ | ||
var repeatCount:Int /*<--zero means infinite, not at the moment it seems*/ | ||
var curRepeatCount:Int = 0 | ||
init(initValues:LoopAnimator2.InitLoopValues = LoopAnimator2.initLoopValues, easing:@escaping EasingEquation = Easing.linear.ease, closure: @escaping FrameTick = {_ in}) { | ||
self.repeatCount = initValues.repeatCount | ||
super.init(initValues: (initValues.duration,initValues.from,initValues.to), easing: easing, closure: closure) | ||
} | ||
/** | ||
* Fires on every frame tick | ||
*/ | ||
override func onFrame(){ | ||
let val:CGFloat = easing(currentFrameCount, from, to-from, framesToEnd) | ||
frameTick(val)/*call the FrameTick method*/ | ||
if(currentFrameCount >= framesToEnd){ | ||
self.currentFrameCount = 0/*reset*/ | ||
if(curRepeatCount >= repeatCount){/*The loop ended*/ | ||
curRepeatCount = 0/*reset*/ | ||
stop()/*stop animation*/ | ||
completed() | ||
} | ||
curRepeatCount += 1 | ||
} | ||
self.currentFrameCount += 1 | ||
} | ||
} | ||
extension LoopAnimator2{ | ||
typealias InitLoopValues = (duration:CGFloat,from:CGFloat,to:CGFloat,repeatCount:Int)/*Signature for initValues*/ | ||
static var initLoopValues:InitLoopValues = (duration:0.5,from:0,to:1,repeatCount:3)/*Default init values*/ | ||
} |
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
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
File renamed without changes.
Oops, something went wrong.