| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- /**
- Copyright (c) 2014-present, Facebook, Inc.
- All rights reserved.
-
- This source code is licensed under the BSD-style license found in the
- LICENSE file in the root directory of this source tree. An additional grant
- of patent rights can be found in the PATENTS file in the same directory.
- */
- #import "POPAnimation.h"
- #import <QuartzCore/CAMediaTimingFunction.h>
- #import "POPAction.h"
- #import "POPAnimationRuntime.h"
- #import "POPAnimationTracerInternal.h"
- #import "POPSpringSolver.h"
- using namespace POP;
- /**
- Enumeration of supported animation types.
- */
- enum POPAnimationType
- {
- kPOPAnimationSpring,
- kPOPAnimationDecay,
- kPOPAnimationBasic,
- kPOPAnimationCustom,
- };
- typedef struct
- {
- CGFloat progress;
- bool reached;
- } POPProgressMarker;
- typedef void (^POPAnimationDidStartBlock)(POPAnimation *anim);
- typedef void (^POPAnimationDidReachToValueBlock)(POPAnimation *anim);
- typedef void (^POPAnimationCompletionBlock)(POPAnimation *anim, BOOL finished);
- typedef void (^POPAnimationDidApplyBlock)(POPAnimation *anim);
- @interface POPAnimation()
- - (instancetype)_init;
- @property (assign, nonatomic) SpringSolver4d *solver;
- @property (readonly, nonatomic) POPAnimationType type;
- /**
- The current animation value, updated while animation is progressing.
- */
- @property (copy, nonatomic, readonly) id currentValue;
- /**
- An array of optional progress markers. For each marker specified, the animation delegate will be informed when progress meets or exceeds the value specified. Specifying values outside of the [0, 1] range will give undefined results.
- */
- @property (copy, nonatomic) NSArray *progressMarkers;
- /**
- Return YES to indicate animation should continue animating.
- */
- - (BOOL)_advance:(id)object currentTime:(CFTimeInterval)currentTime elapsedTime:(CFTimeInterval)elapsedTime;
- /**
- Subclass override point to append animation description.
- */
- - (void)_appendDescription:(NSMutableString *)s debug:(BOOL)debug;
- @end
- NS_INLINE NSString *describe(VectorConstRef vec)
- {
- return NULL == vec ? @"null" : vec->toString();
- }
- NS_INLINE Vector4r vector4(VectorConstRef vec)
- {
- return NULL == vec ? Vector4r::Zero() : vec->vector4r();
- }
- NS_INLINE Vector4d vector4d(VectorConstRef vec)
- {
- if (NULL == vec) {
- return Vector4d::Zero();
- } else {
- return vec->vector4r().cast<double>();
- }
- }
- NS_INLINE bool vec_equal(VectorConstRef v1, VectorConstRef v2)
- {
- if (v1 == v2) {
- return true;
- }
- if (!v1 || !v2) {
- return false;
- }
- return *v1 == *v2;
- }
- NS_INLINE CGFloat * vec_data(VectorRef vec)
- {
- return NULL == vec ? NULL : vec->data();
- }
- template<class T>
- struct ComputeProgressFunctor {
- CGFloat operator()(const T &value, const T &start, const T &end) const {
- return 0;
- }
- };
- template<>
- struct ComputeProgressFunctor<Vector4r> {
- CGFloat operator()(const Vector4r &value, const Vector4r &start, const Vector4r &end) const {
- CGFloat s = (value - start).squaredNorm(); // distance from start
- CGFloat e = (value - end).squaredNorm(); // distance from end
- CGFloat d = (end - start).squaredNorm(); // distance from start to end
-
- if (0 == d) {
- return 1;
- } else if (s > e) {
- // s -------- p ---- e OR s ------- e ---- p
- return sqrtr(s/d);
- } else {
- // s --- p --------- e OR p ---- s ------- e
- return 1 - sqrtr(e/d);
- }
- }
- };
- struct _POPAnimationState;
- struct _POPDecayAnimationState;
- struct _POPPropertyAnimationState;
- extern _POPAnimationState *POPAnimationGetState(POPAnimation *a);
- #define FB_FLAG_GET(stype, flag, getter) \
- - (BOOL)getter { \
- return ((stype *)_state)->flag; \
- }
- #define FB_FLAG_SET(stype, flag, mutator) \
- - (void)mutator (BOOL)value { \
- if (value == ((stype *)_state)->flag) \
- return; \
- ((stype *)_state)->flag = value; \
- }
- #define DEFINE_RW_FLAG(stype, flag, getter, mutator) \
- FB_FLAG_GET (stype, flag, getter) \
- FB_FLAG_SET (stype, flag, mutator)
- #define FB_PROPERTY_GET(stype, property, ctype) \
- - (ctype)property { \
- return ((stype *)_state)->property; \
- }
- #define FB_PROPERTY_SET(stype, property, mutator, ctype, ...) \
- - (void)mutator (ctype)value { \
- if (value == ((stype *)_state)->property) \
- return; \
- ((stype *)_state)->property = value; \
- __VA_ARGS__ \
- }
- #define FB_PROPERTY_SET_OBJ_COPY(stype, property, mutator, ctype, ...) \
- - (void)mutator (ctype)value { \
- if (value == ((stype *)_state)->property) \
- return; \
- ((stype *)_state)->property = [value copy]; \
- __VA_ARGS__ \
- }
- #define DEFINE_RW_PROPERTY(stype, flag, mutator, ctype, ...) \
- FB_PROPERTY_GET (stype, flag, ctype) \
- FB_PROPERTY_SET (stype, flag, mutator, ctype, __VA_ARGS__)
- #define DEFINE_RW_PROPERTY_OBJ(stype, flag, mutator, ctype, ...) \
- FB_PROPERTY_GET (stype, flag, ctype) \
- FB_PROPERTY_SET (stype, flag, mutator, ctype, __VA_ARGS__)
- #define DEFINE_RW_PROPERTY_OBJ_COPY(stype, flag, mutator, ctype, ...) \
- FB_PROPERTY_GET (stype, flag, ctype) \
- FB_PROPERTY_SET_OBJ_COPY (stype, flag, mutator, ctype, __VA_ARGS__)
- /**
- Internal delegate definition.
- */
- @interface NSObject (POPAnimationDelegateInternal)
- - (void)pop_animation:(POPAnimation *)anim didReachProgress:(CGFloat)progress;
- @end
- struct _POPAnimationState
- {
- id __unsafe_unretained self;
- POPAnimationType type;
- NSString *name;
- NSUInteger ID;
- CFTimeInterval beginTime;
- CFTimeInterval startTime;
- CFTimeInterval lastTime;
- id __weak delegate;
- POPAnimationDidStartBlock animationDidStartBlock;
- POPAnimationDidReachToValueBlock animationDidReachToValueBlock;
- POPAnimationCompletionBlock completionBlock;
- POPAnimationDidApplyBlock animationDidApplyBlock;
- NSMutableDictionary *dict;
- POPAnimationTracer *tracer;
- CGFloat progress;
- NSInteger repeatCount;
-
- bool active:1;
- bool paused:1;
- bool removedOnCompletion:1;
-
- bool delegateDidStart:1;
- bool delegateDidStop:1;
- bool delegateDidProgress:1;
- bool delegateDidApply:1;
- bool delegateDidReachToValue:1;
-
- bool additive:1;
- bool didReachToValue:1;
- bool tracing:1; // corresponds to tracer started
- bool userSpecifiedDynamics:1;
- bool autoreverses:1;
- bool repeatForever:1;
- bool customFinished:1;
- _POPAnimationState(id __unsafe_unretained anim) :
- self(anim),
- type((POPAnimationType)0),
- name(nil),
- ID(0),
- beginTime(0),
- startTime(0),
- lastTime(0),
- delegate(nil),
- animationDidStartBlock(nil),
- animationDidReachToValueBlock(nil),
- completionBlock(nil),
- animationDidApplyBlock(nil),
- dict(nil),
- tracer(nil),
- progress(0),
- repeatCount(0),
- active(false),
- paused(true),
- removedOnCompletion(true),
- delegateDidStart(false),
- delegateDidStop(false),
- delegateDidProgress(false),
- delegateDidApply(false),
- delegateDidReachToValue(false),
- additive(false),
- didReachToValue(false),
- tracing(false),
- userSpecifiedDynamics(false),
- autoreverses(false),
- repeatForever(false),
- customFinished(false) {}
-
- virtual ~_POPAnimationState()
- {
- name = nil;
- dict = nil;
- tracer = nil;
- animationDidStartBlock = NULL;
- animationDidReachToValueBlock = NULL;
- completionBlock = NULL;
- animationDidApplyBlock = NULL;
- }
-
- bool isCustom() {
- return kPOPAnimationCustom == type;
- }
-
- bool isStarted() {
- return 0 != startTime;
- }
- id getDelegate() {
- return delegate;
- }
-
- void setDelegate(id d) {
- if (d != delegate) {
- delegate = d;
- delegateDidStart = [d respondsToSelector:@selector(pop_animationDidStart:)];
- delegateDidStop = [d respondsToSelector:@selector(pop_animationDidStop:finished:)];
- delegateDidProgress = [d respondsToSelector:@selector(pop_animation:didReachProgress:)];
- delegateDidApply = [d respondsToSelector:@selector(pop_animationDidApply:)];
- delegateDidReachToValue = [d respondsToSelector:@selector(pop_animationDidReachToValue:)];
- }
- }
- bool getPaused() {
- return paused;
- }
-
- void setPaused(bool f) {
- if (f != paused) {
- paused = f;
- if (!paused) {
- reset(false);
- }
- }
- }
-
- CGFloat getProgress() {
- return progress;
- }
-
- /* returns true if started */
- bool startIfNeeded(id obj, CFTimeInterval time, CFTimeInterval offset)
- {
- bool started = false;
-
- // detect start based on time
- if (0 == startTime && time >= beginTime + offset) {
-
- // activate & unpause
- active = true;
- setPaused(false);
-
- // note start time
- startTime = lastTime = time;
- started = true;
- }
- // ensure values for running animation
- bool running = active && !paused;
- if (running) {
- willRun(started, obj);
- }
- // handle start
- if (started) {
- handleDidStart();
- }
- return started;
- }
- void stop(bool removing, bool done) {
- if (active)
- {
- // delegate progress one last time
- if (done) {
- delegateProgress();
- }
-
- if (removing) {
- active = false;
- }
-
- handleDidStop(done);
- } else {
-
- // stopped before even started
- // delegate start and stop regardless; matches CA behavior
- if (!isStarted()) {
- handleDidStart();
- handleDidStop(false);
- }
- }
-
- setPaused(true);
- }
-
- virtual void handleDidStart()
- {
- if (delegateDidStart) {
- ActionEnabler enabler;
- [delegate pop_animationDidStart:self];
- }
- POPAnimationDidStartBlock block = animationDidStartBlock;
- if (block != NULL) {
- ActionEnabler enabler;
- block(self);
- }
-
- if (tracing) {
- [tracer didStart];
- }
- }
-
- void handleDidStop(BOOL done)
- {
- if (delegateDidStop) {
- ActionEnabler enabler;
- [delegate pop_animationDidStop:self finished:done];
- }
-
- // add another strong reference to completion block before callout
- POPAnimationCompletionBlock block = completionBlock;
- if (block != NULL) {
- ActionEnabler enabler;
- block(self, done);
- }
-
- if (tracing) {
- [tracer didStop:done];
- }
- }
- /* virtual functions */
- virtual bool isDone() {
- if (isCustom()) {
- return customFinished;
- }
-
- return false;
- }
-
- bool advanceTime(CFTimeInterval time, id obj) {
- bool advanced = false;
- bool computedProgress = false;
- CFTimeInterval dt = time - lastTime;
- switch (type) {
- case kPOPAnimationSpring:
- advanced = advance(time, dt, obj);
- break;
- case kPOPAnimationDecay:
- advanced = advance(time, dt, obj);
- break;
- case kPOPAnimationBasic: {
- advanced = advance(time, dt, obj);
- computedProgress = true;
- break;
- }
- case kPOPAnimationCustom: {
- customFinished = [self _advance:obj currentTime:time elapsedTime:dt] ? false : true;
- advanced = true;
- break;
- }
- default:
- break;
- }
-
- if (advanced) {
-
- // estimate progress
- if (!computedProgress) {
- computeProgress();
- }
-
- // delegate progress
- delegateProgress();
-
- // update time
- lastTime = time;
- }
-
- return advanced;
- }
-
- virtual void willRun(bool started, id obj) {}
- virtual bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) { return false; }
- virtual void computeProgress() {}
- virtual void delegateProgress() {}
- virtual void delegateApply() {
- if (delegateDidApply) {
- ActionEnabler enabler;
- [delegate pop_animationDidApply:self];
- }
- POPAnimationDidApplyBlock block = animationDidApplyBlock;
- if (block != NULL) {
- ActionEnabler enabler;
- block(self);
- }
- }
-
- virtual void reset(bool all) {
- startTime = 0;
- lastTime = 0;
- }
- };
- typedef struct _POPAnimationState POPAnimationState;
- @interface POPAnimation ()
- {
- @protected
- struct _POPAnimationState *_state;
- }
- @end
- // NSProxy extensions, for testing pursposes
- @interface NSProxy (POP)
- - (void)pop_addAnimation:(POPAnimation *)anim forKey:(NSString *)key;
- - (void)pop_removeAllAnimations;
- - (void)pop_removeAnimationForKey:(NSString *)key;
- - (NSArray *)pop_animationKeys;
- - (POPAnimation *)pop_animationForKey:(NSString *)key;
- @end
|