iOS开源:AXAnimationChain - 链式动画库

skybix 8年前
   <p><img src="https://simg.open-open.com/show/7ca679f33e84192f2f417daeafcdaa52.jpg"></p>    <h2>Summary</h2>    <p>AXAnimationChain是一个 <strong> 链式动画库 </strong> ,可以用来轻松的创建基于 CAAnimation 的链式动画。 <strong>链</strong> 的组合方式有两种,一种是 <strong>组合</strong> ,另一种则是 <strong>链接</strong> ,通过以上两种方式创建的动画,既可以同时进行,也可以按时间先后进行,可以使用较少的代码创建出丰富复杂的动画效果:</p>    <p>简单使用:</p>    <pre>  <code class="language-objectivec">_transitionView.spring.centerBy(CGPointMake(0, 100)).easeOut.spring.sizeBy(CGSizeMake(100, 100)).spring.cornerRadiusBy(4).animate();</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a25142e808b073175343e3d0c09b26c9.gif"></p>    <p>高级使用(比较冗余):</p>    <pre>  <code class="language-objectivec">_transitionView.chainAnimator.basic.target(self).complete(@selector(complete:)).property(@"position").toValue([NSValue valueWithCGPoint:CGPointMake(100, self.view.center.y)]).easeInBack.duration(0.5).combineSpring.target(self).complete(@selector(complete:)).property(@"bounds").toValue([NSValue valueWithCGRect:CGRectMake(0, 0, 100, 100)]).duration(0.5).repeatCount(5).autoreverses.combineSpring.target(self).complete(@selector(complete:)).property(@"transform.rotation").toValue(@(M_PI_4)).duration(0.5).repeatCount(3).beginTime(1.0).autoreverses.nextToBasic.property(@"position").toValue([NSValue valueWithCGPoint:self.view.center]).duration(0.5).combineSpring.property(@"bounds").toValue([NSValue valueWithCGRect:CGRectMake(0, 0, 100, 100)]).duration(0.8).nextToBasic.property(@"transform.rotation").toValue(@(M_PI_4)).duration(1.0).completeWithBlock(nil).animate();      self.view.spring.backgroundColorTo([UIColor colorWithRed:1.000 green:0.988 blue:0.922 alpha:1.00]).animate();</code></pre>    <p>看起来比较冗余,但是细读会发现,其实就只有 <strong>一行代码</strong> .</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a11269bc5cef96d2601807781327060e.gif"></p>    <p>链接和 <strong>组合</strong> 在协议 AXAnimatorChainDelegate 中进行定义,分别是: nextTo: 和 combineWith: ,在使用的过程中应当予以区分.</p>    <p>AXAnimationChain 基于 CoreAnimation 定义了几种 Animator , AXChainAnimator 是基类,预定义了一系列 Animate 操作,可以 <strong>链接</strong> 、 <strong>组合</strong> 并且控制动画完成的 <strong>回调</strong> :</p>    <pre>  <code class="language-objectivec">AXChainAnimator    --AXBasicChainAnimator     ==CABasicAnimation      --AXSpringChainAnimator  ==CASpringAnimation    --AXKeyframeChainAnimator  ==CAKeyframeAnimation    --AXTransitionChainAnimator==CATransitionAnimation</code></pre>    <h3>Next-To</h3>    <p>通过链接的方式处理两个 animator , <strong>被链接</strong> 的 animator 将会在前者动画(包含 <em> 组合 </em> 的动画)完成之后进行动画, 大概的示例如下:</p>    <pre>  <code class="language-objectivec">[former nextTo:nexter];</code></pre>    <p>Next-To 方法的原型如下:</p>    <pre>  <code class="language-objectivec">- (instancetype)nextTo:(id<AXAnimatorChainDelegate>)animator;</code></pre>    <p>当向 former aniamtor 发送 nextTo: 消息之后,返回的是 nexter animator 作为下次 <strong>链接</strong> 或者 <strong>组合</strong> 操作的对象,因此 AXAnimationChain 定义了几种常用的操作:</p>    <pre>  <code class="language-objectivec">/// 链接到Basic动画并且返回链接的Basic动画.  - (AXBasicChainAnimator *)nextToBasic;  /// 链接到Spring动画并且放回链接的Spring动画.  - (AXSpringChainAnimator *)nextToSpring;  /// 链接到Keyframe动画并且放回链接的Keyframe动画.  - (AXKeyframeChainAnimator *)nextToKeyframe;  /// 链接到Transition动画并且返回链接的Transition动画.  - (AXTransitionChainAnimator *)nextToTransition;</code></pre>    <p>在发送消息之后分别返回对应类型的 <strong>可操作对象</strong> .</p>    <h3>Combine-With</h3>    <p>通过组合的方式处理两个 animator ,被组合的 animator 将会与前者动画同时进行,完成的时间以时间最长的为准, 示例如下:</p>    <pre>  <code class="language-objectivec">[former combineWith:combiner];</code></pre>    <p>Combine-With 方法原型如下:</p>    <pre>  <code class="language-objectivec">- (instancetype)combineWith:(nonnull AXChainAnimator *)animator;</code></pre>    <p>当向 former animator 发送 combineWith: 消息之后,返回的是 combiner animator 作为下次 <strong>链接</strong> 或者 <strong>组合</strong> 操作的对象,在 AXAnimationChain 中,默认一下几种组合方式:</p>    <pre>  <code class="language-objectivec">/// 组合到Basic动画并且返回组合的Basic动画.  - (AXBasicChainAnimator *)combineBasic;  /// 组合到Spring动画并且放回组合的Spring动画.  - (AXSpringChainAnimator *)combineSpring;  /// 组合到Keyframe动画并且放回组合的Keyframe动画.  - (AXKeyframeChainAnimator *)combineKeyframe;  /// 组合到Transition动画并且返回组合的Transition动画.  - (AXTransitionChainAnimator *)combineTransition;</code></pre>    <p>同样的,在向某一操作对象 animator 发送以上消息之后,将会分别返回对应类型的 <strong>可操作对象</strong> .</p>    <h3>Relationship</h3>    <p>在 AXAnimationChain 中,关系的管理采用的是二叉树的理论. 某一个 animator 对应的类结构中,包含了指向 <strong>父节点</strong> 的 superAnimator 用于表示 父animator , 表示此 animator 为 superAnimator 所链接的 animator , 此时, superAnimator 的 childAnimator 即指向此 animator 作为一个 <strong>闭环链</strong> 将两者的关系锁定起来; 同样的,某一个 animator 还拥有一个指向 <strong>兄弟节点</strong> 的 NSArray<AXChainAnimator *> 结构: combinedAnimators 用于管理所组合的 animators ,并且,被组合的 animator 的父节点 superAnimator 则指向当前 animator .</p>    <pre>  <code class="language-objectivec">- (void)start {      NSAssert(_animatedView, @"Animation chain cannot be created because animated view is null.");      AXChainAnimator *superAnimator = _superAnimator;      AXChainAnimator *superSuperAnimator = _superAnimator;      while (superAnimator) {          superAnimator = superAnimator.superAnimator;          if (superAnimator) {              superSuperAnimator = superAnimator;          }      }      if (superSuperAnimator) {          [superSuperAnimator start];      } else {          [self _beginAnimating];          if (!_childAnimator) [self _clear];      }  }</code></pre>    <p>AXAnimatioChain 就是通过这样的关系把所有 <strong>链接</strong> 和 <strong>组合</strong> 的 animator 管理起来的,在完成关系的链接或组合之后,需要向最后一个 animator 发送 -start 消息动画才能正常进行. animator 在接收到 -start 消息之后,会逐级遍历 superAnimator 直至 superAnimator.superAnimator==nil , 此时获取到 superSuperAnimator , 从 superSuperAnimator 自祖先往下逐级进行动画, <strong>组合</strong> 的动画会 <strong>同时</strong> 进行, <strong>链接</strong> 的动画则按 <strong>顺序</strong> 进行.</p>    <h2>Features</h2>    <p>轻量级解决方案</p>    <p>基于CoreAnimation的封装,安全、高效!</p>    <p>一行代码搞定复杂的动画管理,提高代码维护效</p>    <p>TimingControl</p>    <p>时间曲线,时间曲线用于描述动画随时间进行的速度, AXAnimationChain 除了包含系统默认的时间曲线之外,还提供了如下的曲线以呈现更漂亮的动画:</p>    <p><img src="https://simg.open-open.com/show/3857c066afd4a1822b2506a0cb79054e.jpg"></p>    <p>AXSpringAnimation</p>    <p>CoreAnimation 自 iOS2.0 就为iOS平台提供了核心动画的支持,但是在iOS9.0之前,一直没有 Spring 动画,要使用 Spring 动画要么使用第三方动画库,要么使用系统提供的方法:</p>    <pre>  <code class="language-objectivec">+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);</code></pre>    <p>但是系统提供的这个方法也是 iOS7.0 以后才能使用了,并且在控制上并非那么容易.</p>    <p>AXSpringAnimation 是基于 <strong>阻尼震动</strong> 运动模型的 Spring 动画类,能够完美与 CASpringAnimation 相通用:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/6a8c0a615209ccc8df1cf54ce2318fa6.gif"></p>    <p>动画中,左边正方形使用的是 CASpringAnimation 类,右边的则使用的是 AXSpringAnimation ,两者的动画曲线是一致的.</p>    <p>AXSpringAnimation 的API和 CASpringAnimation 是一致的:</p>    <pre>  <code class="language-objectivec">@interface AXSpringAnimation : CAKeyframeAnimation  /* The mass of the object attached to the end of the spring. Must be greater   than 0. Defaults to one. */    @property(assign, nonatomic) CGFloat mass;    /* The spring stiffness coefficient. Must be greater than 0.   * Defaults to 100. */    @property(assign, nonatomic) CGFloat stiffness;    /* The damping coefficient. Must be greater than or equal to 0.   * Defaults to 10. */    @property(assign, nonatomic) CGFloat damping;    /* The initial velocity of the object attached to the spring. Defaults   * to zero, which represents an unmoving object. Negative values   * represent the object moving away from the spring attachment point,   * positive values represent the object moving towards the spring   * attachment point. */    @property(assign, nonatomic) CGFloat initialVelocity;    /* Returns the estimated duration required for the spring system to be   * considered at rest. The duration is evaluated for the current animation   * parameters. */    @property(readonly, nonatomic) CFTimeInterval settlingDuration;    /* The objects defining the property values being interpolated between.   * All are optional, and no more than two should be non-nil. The object   * type should match the type of the property being animated (using the   * standard rules described in CALayer.h). The supported modes of   * animation are:   *   * - both `fromValue' and `toValue' non-nil. Interpolates between   * `fromValue' and `toValue'.   *   * - `fromValue' and `byValue' non-nil. Interpolates between   * `fromValue' and `fromValue' plus `byValue'.   *   * - `byValue' and `toValue' non-nil. Interpolates between `toValue'   * minus `byValue' and `toValue'. */    @property(nullable, strong, nonatomic) id fromValue;  @property(nullable, strong, nonatomic) id toValue;  @property(nullable, strong, nonatomic) id byValue;  @end</code></pre>    <p>Convertable</p>    <p>AXAnimationChain 框架还提供了将 CABasicAnimation 无缝转换为 CAKeyframeAnimation 的功能:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ca6aa3abbe4f03e2b8798f4cd4853a8f.gif"></p>    <p>动画中,左边是 CABasicAnimation ,右边是 CAKeyframeAnimation ,两者对应的动画曲线是一致的.</p>    <p>要使用动画转换,请参考:</p>    <pre>  <code class="language-objectivec">#import <QuartzCore/QuartzCore.h>  #import <UIKit/UIKit.h>  #import "CAMediaTimingFunction+Extends.h"    @interface CAAnimation (Convertable)  @end    @interface CAKeyframeAnimation (Convertable)  + (instancetype)animationWithBasic:(CABasicAnimation *)basicAnimation;  + (instancetype)animationWithBasic:(CABasicAnimation *)basicAnimation usingValuesFunction:(double (^)(double t, double b, double c, double d))valuesFunction;  @end</code></pre>    <h2>Requirements</h2>    <p>AXAnimationChain 对系统版本支持到 iOS8.0 ,需要使用到的框架:</p>    <ul>     <li> <p>Foundation.framework</p> </li>     <li> <p>UIKit.framework</p> </li>     <li>QuartzCore.framework</li>    </ul>    <p>使用的时候最好使用最新版Xcode.</p>    <h2>Adding AXAimationChain To Your Project</h2>    <h3>CocoaPods</h3>    <p>CocoaPods is the recommended way to add AXWebViewController to your project.</p>    <ol>     <li>Add a pod entry for AXPopoverView to your Podfile pod 'AXAimationChain', '~> 0.1.0'</li>     <li>Install the pod(s) by running pod install .</li>     <li>Include AXPopoverView wherever you need it with #import "AXAimationChain.h" .</li>     <li>若需要单独使用 AXSpringAnimation 或者 Convertable 以及 TimingControl 等特性的话,只需要将podfile里边 AXAnimationChain 替换为 AXAnimationChain/CoreAnimation 即可,即: pod 'AXAimationChain/CoreAnimation', '~> 0.1.0' .</li>    </ol>    <h3>Source files</h3>    <p>Alternatively you can directly add all the source files to your project.</p>    <ol>     <li>Download the latest code version or add the repository as a git submodule to your git-tracked project. </li>     <li>Open your project in Xcode, then drag and drop the source group onto your project (use the "Product Navigator view"). Make sure to select Copy items when asked if you extracted the code archive outside of your project. </li>     <li>Include AXPopoverView wherever you need it with #import "AXAimationChain.h" .</li>    </ol>    <h2>License</h2>    <p>This code is distributed under the terms and conditions of the MIT license . </p>    <h2>使用</h2>    <p>请参考示例工程代码以及API.</p>    <h2>不足</h2>    <p>此项目在开展的时候比较庞大,基础的核心类已经构建好了,基本目标已经达成,但是还有很多需要完善的地方,后边会逐步完善并发布Release版本.</p>    <h2> </h2>    <p> </p>