CoreAnimation核心动画介绍
<h2><strong>1. 简介</strong></h2> <ul> <li> <p>Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍。也就是说,使用少量的代码就可以实现非常强大的功能。</p> </li> <li> <p>Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程,即动画的时候可以点击(按钮)。</p> </li> <li> <p>要注意的是,Core Animation是直接作用在CALayer上的,并非UIView</p> </li> </ul> <p style="text-align:center"><img src="https://simg.open-open.com/show/4cd37d1cd6eaa5315da35c863cc4defb.jpg"></p> <p> </p> <h2><strong>2. 核心动画类的层次结构</strong></h2> <p>核心动画位于CAAnimation.h中,该文件提供了以下接口及类层级结构</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/751d9a6b27091ae2eedb821ff1e02b4a.jpg"></p> <p style="text-align:center">CAAnimation类继承关系.jpeg</p> <p>如上图所示,CoreAnimation所使用类如下:</p> <ul> <li><strong>CAAnimation</strong> :该类是所有动画的抽象类,不能直接使用,只能使用其子类。并且实现了CAMediaTiming协议;</li> <li><strong>CAPropertyAnimation</strong> :是CAAnimation的子类,支持动画</li> <li><strong>CAMediaTiming</strong> :CAAnimation实现的协议,共有8个属性控制动画的过程;</li> <li><strong>CABasicAnimation</strong> :基本动画-</li> <li><strong>CATransition</strong> :转场动画</li> <li><strong>CAKeyframeAnimation</strong> :关键帧动画</li> <li><strong>CAAnimationGroup</strong> :组动画</li> <li><strong>CASpringAnimation</strong> :弹簧动画</li> </ul> <h2><strong>3. CAMediaTiming协议</strong></h2> <p>CAMediaTiming协议共有8个属性,每个属性简单介绍如下</p> <table> <thead> <tr> <th>属性</th> <th>说明</th> </tr> </thead> <tbody> <tr> <td>duration</td> <td>动画持续时间</td> </tr> <tr> <td>beginTime</td> <td>动画相对一个对象开始的时间,起到延时执行的目的</td> </tr> <tr> <td>speed</td> <td>动画执行速度</td> </tr> <tr> <td>timeOffset</td> <td>动画执行偏移</td> </tr> <tr> <td>repeatCount</td> <td>动画执行重复次数</td> </tr> <tr> <td>repeatDuration</td> <td>动画执行时间</td> </tr> <tr> <td>autoreverses</td> <td>动画结束后是否反向执行</td> </tr> <tr> <td>fillMode</td> <td>决定当前对象在非active时间段的行为</td> </tr> </tbody> </table> <h3><strong>3.1. duration</strong></h3> <p>代码如下:</p> <pre> <code class="language-objectivec">#import "ViewController.h" #define kScreenW ([UIScreen mainScreen].bounds.size.width) #define kScreenH ([UIScreen mainScreen].bounds.size.height) @interface ViewController () @property (nonatomic,weak) CALayer *redLayer; @property (nonatomic,weak) CALayer *blueLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //1 添加一个红色的Layer CALayer *redLayer = [CALayer layer]; self.redLayer = redLayer; redLayer.backgroundColor = [UIColor redColor].CGColor; redLayer.frame = CGRectMake(0, 0, kScreenW/2.0, 50); [self.view.layer addSublayer:redLayer]; //1 添加一个蓝色的Layer CALayer *blueLayer =[CALayer layer]; self.blueLayer = blueLayer; blueLayer.backgroundColor =[UIColor blueColor].CGColor; blueLayer.frame = CGRectMake(kScreenW/2.0, 0, kScreenW/2.0, 50); [self.view.layer addSublayer:blueLayer]; } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //给红色Layer添加动画 CABasicAnimation *redLayerBasicAnimation = [CABasicAnimation animation]; redLayerBasicAnimation.keyPath=@"position"; redLayerBasicAnimation.fromValue=[NSValue valueWithCGPoint:self.redLayer.position]; redLayerBasicAnimation.toValue=[NSValue valueWithCGPoint:CGPointMake(self.redLayer.position.x, kScreenH)]; //设置动画持续时间 redLayerBasicAnimation.duration = 10; [self.redLayer addAnimation:redLayerBasicAnimation forKey:nil]; //给蓝色Layer添加动画 CABasicAnimation *blueLayerBasicAnimation = [CABasicAnimation animation]; blueLayerBasicAnimation.keyPath=@"position"; blueLayerBasicAnimation.fromValue=[NSValue valueWithCGPoint:self.blueLayer.position]; blueLayerBasicAnimation.toValue=[NSValue valueWithCGPoint:CGPointMake(self.blueLayer.position.x, kScreenH)]; //设置动画持续时间 blueLayerBasicAnimation.duration = 5; [self.blueLayer addAnimation:blueLayerBasicAnimation forKey:nil]; } @end</code></pre> <p>上述代码在视图上添加两个动画,其中一个动画持续10s,另一个动画持续5秒,竖向位移为屏幕高</p> <p>执行效果如下</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/1e3365d6aa2c40f25fdda19102b98a9e.gif"></p> <p style="text-align:center">动画持续时间左10右5.gif</p> <h3><strong>3.2. beginTime</strong></h3> <p>beginTime为相对时间系开始的时间,可以认为是起到延时左右,设置右侧蓝色Layer相对当前时间系延时5s时间执行</p> <pre> <code class="language-objectivec">//相对于当前时间系延后5秒,起到延时作用 blueLayerBasicAnimation.beginTime = CACurrentMediaTime()+5;</code></pre> <p>执行效果显示蓝色相对红色延时5秒后执行,且同时到达底</p> <p>端</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/f5a548c49db560cd804caae27cfec6c0.gif"></p> <p style="text-align:center">延时5秒执行.gif</p> <h3><strong>3.3. speed</strong></h3> <p>speed为相对一个时间系,本Layer时间流逝速度,默认情况为1,设置蓝色Layer时间流逝速度为0.5倍则在不设置时间偏移的情况下两者同时到达低端,效果如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/91694282dbbc76c38caf43a9961813db.gif"></p> <p style="text-align:center">蓝色Layer0.5倍流逝速度.gif</p> <h3><strong>3.4. timeOffset</strong></h3> <p>该属性主要标记一个动画从何处执行,该属性不存在延迟作用。例如一个动画正常执行如下顺序:</p> <pre> <code class="language-objectivec">A --> B --> C --> D --> A</code></pre> <p>此时如果设置 timeOffset 为B状态,则该动画开始执行状态为</p> <pre> <code class="language-objectivec">B --> C --> D --> A --> B</code></pre> <p>设置红色动画持续时间10s,蓝色动画持续时间5秒,设置蓝色动画的timeOffset为2.5秒</p> <pre> <code class="language-objectivec">//相对于当前时间系延后5秒,起到延时作用 //blueLayerBasicAnimation.beginTime = CACurrentMediaTime()+5; //设置时间流逝速度 //blueLayerBasicAnimation.speed = 0.5; //设置动画偏移时间 blueLayerBasicAnimation.timeOffset =CACurrentMediaTime()+2.5;</code></pre> <p>运行效果如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/3e728d5c3dfcc36f828de7094f9b99e9.gif"></p> <p style="text-align:center">设置动画偏移时间.gif</p> <h3><strong>3.5. repeatCount</strong></h3> <p>repeatCount该属性主要记录动画重复持续次数,设定红色Layer动画持续时间为10s,蓝色为5s,新添加蓝色Layer重复次数为2次,代码如下:</p> <pre> <code class="language-objectivec">blueLayerBasicAnimation.duration = 5; //相对于当前时间系延后5秒,起到延时作用 //blueLayerBasicAnimation.beginTime = CACurrentMediaTime()+5; //设置时间流逝速度 //blueLayerBasicAnimation.speed = 0.5; //设置动画偏移时间 //blueLayerBasicAnimation.timeOffset =CACurrentMediaTime()+2.5; //设置动画重复次数 blueLayerBasicAnimation.repeatCount = 2;</code></pre> <p>效果如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/74afb68de379c1f8aca2ceedcc9796a3.gif"></p> <p style="text-align:center">蓝色重复次数为2次.gif</p> <h3><strong>3.6. repeatDuration</strong></h3> <p>repeatDuration该属性主要记录动画重复持续时间,设定红色Layer动画持续时间为10s,蓝色为5s,新添加蓝色Layer动画持续时间12.5秒,代码如下:</p> <pre> <code class="language-objectivec">blueLayerBasicAnimation.duration = 5; //相对于当前时间系延后5秒,起到延时作用 //blueLayerBasicAnimation.beginTime = CACurrentMediaTime()+5; //设置时间流逝速度 //blueLayerBasicAnimation.speed = 0.5; //设置动画偏移时间 //blueLayerBasicAnimation.timeOffset =CACurrentMediaTime()+2.5; //设置动画重复次数 //blueLayerBasicAnimation.repeatCount = 2; //设置动画持续时间 blueLayerBasicAnimation.repeatDuration = 12.5;</code></pre> <p>运行效果如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/c378f3933d0afd31cb5d2d01ce811453.gif"></p> <p style="text-align:center">蓝色持续12.5秒.gif</p> <h3><strong>3.7. autoreverses</strong></h3> <p>该属性主要执行结束动画后是否执行逆行动画过程,默认NO不执行,如果YES则执行,</p> <pre> <code class="language-objectivec">blueLayerBasicAnimation.duration = 5; //相对于当前时间系延后5秒,起到延时作用 //blueLayerBasicAnimation.beginTime = CACurrentMediaTime()+5; //设置时间流逝速度 //blueLayerBasicAnimation.speed = 0.5; //设置动画偏移时间 //blueLayerBasicAnimation.timeOffset =CACurrentMediaTime()+2.5; //设置动画重复次数 //blueLayerBasicAnimation.repeatCount = 2; //设置动画持续时间 //blueLayerBasicAnimation.repeatDuration = 12.5; //动画 blueLayerBasicAnimation.autoreverses = YES;</code></pre> <p style="text-align:center"><img src="https://simg.open-open.com/show/bbbfd967eaa4679e7dcb359d13d3053b.gif"></p> <p style="text-align:center">是否执行逆行动画.gif</p> <h3><strong>3.8. fillMode</strong></h3> <p>fillMode决定当前对象在非active时间段的行为.比如动画开始之前,动画结束之后 ,但是该属性要和CAAnimation中的 <strong>removedOnCompletion</strong> 属性配合着使用.</p> <pre> <code class="language-objectivec">/* When true, the animation is removed from the render tree once its * active duration has passed. Defaults to YES. */ 如果是true,一旦动画的活动期间过了,那么这个动画将被移除,默认值YES @property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;</code></pre> <p>其中fillMode属性值(要想fillMode有效,最好设置removedOnCompletion=NO)存在四中状态,如下:</p> <pre> <code class="language-objectivec">/* `fillMode' options. */ CA_EXTERN NSString * const kCAFillModeForwards __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0); CA_EXTERN NSString * const kCAFillModeBackwards __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0); CA_EXTERN NSString * const kCAFillModeBoth __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0); CA_EXTERN NSString * const kCAFillModeRemoved __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);</code></pre> <ul> <li><strong>kCAFillModeForwards</strong> :当动画结束后,layer会保持在动画结束状态 .</li> <li><strong>kCAFillModeBackwards</strong> :当动画结束后,layer会一直保持着动画最后的状态.</li> <li><strong>kCAFillModeBoth</strong> :这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状</li> <li><strong>kCAFillModeRemoved</strong> :这个是 <strong>默认值</strong> ,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态</li> </ul> <p>测试 <strong>kCAFillModeForwards状态</strong> 代码如下:</p> <pre> <code class="language-objectivec">blueLayerBasicAnimation.fillMode = kCAFillModeForwards; blueLayerBasicAnimation.removedOnCompletion = NO;</code></pre> <p>运行效果如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/5e02e15890a8329558ab99e4513597bb.gif"></p> <p style="text-align:center">kCAFillModeForwards状态测试.gif</p> <h2><strong>4.0 CAAnimation抽象类</strong></h2> <p>CAAnimation是Core Animation所有动画类型的抽象基类。作为一个抽象类,CAAnimation本身并没有做多少工作,它提供了一个计时函数(用于动画的进入进出状态),一个委托(用于反馈动画状态)以及一个removedOnCompletion,用于标识动画是否该在结束后自动释放(默认YES,为了防止内存泄露)。CAAnimation同时实现了一些协议,包括CAMediaTiming。</p> <pre> <code class="language-objectivec">/* Creates a new animation object. */ + (instancetype)animation; /* Animations implement the same property model as defined by CALayer. * See CALayer.h for more details. */ + (nullable id)defaultValueForKey:(NSString *)key; - (BOOL)shouldArchiveValueForKey:(NSString *)key; /* A timing function defining the pacing of the animation. Defaults to * nil indicating linear pacing. */ @property(nullable, strong) CAMediaTimingFunction *timingFunction; /* The delegate of the animation. This object is retained for the * lifetime of the animation object. Defaults to nil. See below for the * supported delegate methods. */ @property(nullable, strong) id delegate; /* When true, the animation is removed from the render tree once its * active duration has passed. Defaults to YES. */ @property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;</code></pre> <h3><strong>4.1. +animation方法</strong></h3> <p>该方法为抽象方法,由子类创建实例对象,生成相应的对象类型。例如基本动画、关键帧动画、组动画、转场动画。</p> <h3><strong>4.2. timingFunction属性</strong></h3> <p>Timing Function的会被用于变化起点和终点之间的插值计算.形象点说是Timing Function决定了动画运行的节奏(Pacing),比如是均匀变化(线性变化)、先快后慢、先慢后快、还是先慢再快再慢.</p> <p>时间函数是使用的一段函数来描述的,横座标是时间t取值范围是0.0-1.0,纵座标是变化量x(t)也是取值范围也是0.0-1.0 假设有一个动画,duration是8秒,变化值的起点是a终点是b(假设是透明度),那么在4秒处的值是多少呢? 可以通过计算为 a + x(4/8) (b-a), 为什么这么计算呢?讲实现的时间映射到单位值的时候4秒相对于总时间8秒就是0.5然后可以得到0.5的时候单位变化量是 x(0.5), x(0.5)/1 = 实际变化量/(b-a), 其中b-a为总变化量,所以实际变化量就是x(0.5) (b-a) ,最后4秒时的值就是 a + x(0.5) * (b-a),所以计算的本质是映射</p> <p>Timing Function对应的类是CAMediaTimingFunction,它提供了两种获得时间函数的方式,一种是使用预定义的五种时间函数,一种是通过给点两个控制点得到一个时间函数. 相关的方法为</p> <pre> <code class="language-objectivec">+ (instancetype)functionWithName:(NSString *)name; + (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y; - (instancetype)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y; - (void)getControlPointAtIndex:(size_t)idx values:(float[2])ptr;</code></pre> <p>五种预定义的时间函数名字的常量变量分别为</p> <ul> <li>kCAMediaTimingFunctionLinear,</li> <li>kCAMediaTimingFunctionEaseIn,</li> <li>kCAMediaTimingFunctionEaseOut,</li> <li>kCAMediaTimingFunctionEaseInEaseOut,</li> <li>kCAMediaTimingFunctionDefault.</li> </ul> <p>下图展示了前面四种Timing Function的曲线图,横座标表示时间,纵座标表示变化量,这点需要搞清楚(并不是平面座标系中xy).</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/d3bfb9910807f220c35134ec6d3f889e.jpg"></p> <p style="text-align:center">时间曲线.jpg</p> <p>自定义的Timing Function的函数图像就是一条三次 <a href="/misc/goto?guid=4959647068791430852" rel="nofollow,noindex">贝塞尔曲线Cubic Bezier Curve</a> ,贝塞尔曲线的优点就是光滑,用在这里就使得变化显得光滑.一条三次贝塞尔曲线可以由起点终点以及两个控制点决定.</p> <p>上面的kCAMediaTimingFunctionDefault对应的函数曲线其实就是通过[(0.0,0.0), (0.25,0.1), (0.25,0.1), (1.0,1.0)]这四个点决定的三次贝塞尔曲线,头尾为起点和终点,中间的两个点是控制点.</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a5c5391bccf4d03601d430ca711e8429.jpg"></p> <p style="text-align:center">贝塞尔曲线.jpg</p> <p>上图中P0是起点,P3是终点,P1和P2是两个控制点</p> <h3><strong>4.3. delegate代理属性</strong></h3> <p>CAAnimation提供代理协议,代理方法提供了动画开始和动画结束方法,通过代理方法执行回调。</p> <pre> <code class="language-objectivec">@interface NSObject (CAAnimationDelegate) /* Called when the animation begins its active duration. */ - (void)animationDidStart:(CAAnimation *)anim; /* Called when the animation either completes its active duration or * is removed from the object it is attached to (i.e. the layer). 'flag' * is true if the animation reached the end of its active duration * without being removed. */ - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; @end</code></pre> <h3><strong>4.4. removedOnCompletion</strong></h3> <p>该方法前边已经介绍,用于移除动画使用</p> <h2><strong>5.0. CAPropertyAnimation</strong></h2> <p>CAPropertyAnimation也是个抽象类,本身不具备动画效果,只有子类才有,主要存在以下方法和属性</p> <pre> <code class="language-objectivec">@interface CAPropertyAnimation : CAAnimation + (instancetype)animationWithKeyPath:(nullable NSString *)path; @property(nullable, copy) NSString *keyPath; @property(getter=isAdditive) BOOL additive; @property(getter=isCumulative) BOOL cumulative; @property(nullable, strong) CAValueFunction *valueFunction; @end</code></pre> <h2><strong>6.0 CABasicAnimation基础动画</strong></h2> <p>CABasicAnimation 基础动画其实就是一段时间内发生的改变,最简单的形式就是从一个值改变到另一个值,</p> <pre> <code class="language-objectivec">@property(nullable, strong) id fromValue; @property(nullable, strong) id toValue; @property(nullable, strong) id byValue;</code></pre> <p>下面这段英文摘自苹果官方文档,将的是fromValue toValue ByValue 怎么使用</p> <p>The interpolation values are used as follows:</p> <ul> <li> <p>Both fromValue and toValue are non-nil. Interpolates between fromValue and toValue.</p> <p>翻译:fromValue和toValue都不为空,在fromValue到toValue之间插值</p> </li> <li> <p>fromValue and byValue are non-nil. Interpolates between fromValue and (fromValue + byValue).</p> <p>翻译:fromValue和byValue都不为空,在fromValue到fromValue +byValue之间插值</p> </li> <li>byValue and toValue are non-nil. Interpolates between (toValue - byValue) and toValue.<br> 翻译:byValue和toValue都不为空,在 (toValue - byValue) 到toValue之间插值</li> <li> <p>fromValue is non-nil. Interpolates between fromValue and the current presentation value of the property.</p> <p>翻译:fromValue不为空,在 fromValue到对象当前值之间插值</p> </li> <li> <p>toValue is non-nil. Interpolates between the current value of keyPath in the target layer’s presentation layer andtoValue.</p> <p>翻译:toValue不为空,在 对象当前值到toValue之间插值</p> </li> <li> <p>byValue is non-nil. Interpolates between the current value of keyPath in the target layer’s presentation layer and that value plus byValue.</p> <p>翻译:byValue不为空,在 对象当前值到经过byValue值之间插值</p> </li> <li> <p>All properties are nil. Interpolates between</p> <p>the previous value of keyPath in the target layer’s presentation layer and</p> <p>the current value of keyPath in the target layer’s presentation layer.</p> <p>翻译:都不为空,在 对象以前值到对象当前值之间插值(不懂)</p> <h2><strong>7. CAKeyframeAnimation关键帧动画</strong></h2> <p>CAKeyframeAnimation 关键帧动画,该动画从字面简单的理解为做动画时只需要给出动画部分关键帧即可,系统会通过插值方式自己完成动画,在文件中存在以属性:</p> </li> </ul> <pre> <code class="language-objectivec">/** General keyframe animation class. **/ @interface CAKeyframeAnimation : CAPropertyAnimation @property(nullable, copy) NSArray *values; @property(nullable) CGPathRef path; @property(nullable, copy) NSArray<NSNumber *> *keyTimes; @property(nullable, copy) NSArray<CAMediaTimingFunction *> *timingFunctions; @property(copy) NSString *calculationMode; @property(nullable, copy) NSArray<NSNumber *> *tensionValues; @property(nullable, copy) NSArray<NSNumber *> *continuityValues; @property(nullable, copy) NSArray<NSNumber *> *biasValues; @property(nullable, copy) NSString *rotationMode; @end</code></pre> <h3><strong>7.1 values属性</strong></h3> <p>values属性为NSArray类型,里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧</p> <p>代码如下:</p> <pre> <code class="language-objectivec">CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; animation.keyPath = @"position.y"; animation.values = @[@150,@400,@100,@400]; animation.duration = 5.f; [self.testLayer addAnimation:animation forKey:nil];</code></pre> <p>执行动画效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/57c3219aec48dc82213d36e432db1125.gif"></p> <p style="text-align:center">关键帧动画.gif</p> <h3><strong>7.2 keyTimes属性</strong></h3> <p>keyTimes可以为对应的关键帧指定对应的时间点,其取值范围为0~1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的</p> <p>keyTimes值要和运行时间段吻合,如果不吻合会出现以下问题:</p> <p>第一种情况:吻合情况</p> <pre> <code class="language-objectivec">CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; animation.keyPath = @"position.y"; animation.values = @[@400,@0,@400,@0];//定义了4帧 animation.duration = 5.f; //设置每一个关键帧的时间 animation.keyTimes = @[@0,@(1/8.),@(3/8.),@1]; [self.testLayer addAnimation:animation forKey:nil];</code></pre> <p>效果如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/c65d5494dcd177df98e7e01bf51e2825.gif"></p> <p style="text-align:center">关键帧动画添加keyTimes属性.gif</p> <p>分析:</p> <p>添加keyTimes记录着执行到每一帧动画的时间点,例如:</p> <p>@400 @0 为起始值</p> <p>@400 - > @0过程中,必须在@(1/8.)执行到@0位置</p> <p>@0 - >@400过程中,必须在@(3/8.)执行到@ 400位置</p> <p>@400 - > @0过程中,必须在@1执行到@0位置</p> <p>第二种情况:少给值了,少给则提前结束</p> <pre> <code class="language-objectivec">//设置每一个关键帧的时间 animation.keyTimes = @[@0,@(1/8.),@1];</code></pre> <p>运行效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/4c9460c88f53824c88a1c7a725e78c20.gif"></p> <p style="text-align:center">keyTime值数量过少.gif</p> <p>分析:</p> <p>添加keyTimes记录着执行到每一帧动画的时间点,例如:</p> <p>@400 @0 为起始值</p> <p>@400 - > @0过程中,必须在@(1/8.)执行到@0位置</p> <p>@0 - >@400过程中,必须在@(1)执行到@ 400位置,由于@1时间结束,所以动画直接结束</p> <p>第三种情况:多给值了,则按照顺序执行,剩余时间无效</p> <pre> <code class="language-objectivec">CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; animation.keyPath = @"position.y"; animation.values = @[@400,@0,@400,@0];//定义了4帧 animation.duration = 5.f; //设置每一个关键帧的时间 animation.keyTimes = @[@0,@(1/8.),@(2/8.),@(3/8.),@(4/8.),@1]; [self.testLayer addAnimation:animation forKey:nil];</code></pre> <p>效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/0c0f8b99c10f04ff002eeb9467d8e162.gif"></p> <p style="text-align:center">keyTimes多给情况.gif</p> <p>分析:</p> <p>添加keyTimes记录着执行到每一帧动画的时间点,例如:</p> <p>@400 @0 为起始值</p> <p>@400 - > @0过程中,必须在@(1/8.)执行到@0位置</p> <p>@0 - >@400过程中,必须在@(2/8.)执行到@ 400位置</p> <p>@400 - > @0过程中,必须在@@(3/8.)执行到@0位置</p> <p>剩余时间@(3/8.) -> @1这个时间段属于无效冗余时间,所以动画在等待到@1这一时刻立即结束。</p> <h3><strong>7.3. timingFunctions</strong></h3> <p>该属性在上述已经介绍过了,只不过是在动画每一个执行路径过程中执行的时间类型函数,如果你values个数为N,则timingFunctions 个数至少为N-1;否则剩余部分则执行线性时间函数。</p> <p>代码例子:</p> <pre> <code class="language-objectivec">animation.timingFunctions = @[ [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];</code></pre> <p>运行效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/5f904591f3e753c700c57618f06dbb1e.gif"></p> <p style="text-align:center">timingFunctions时间函数.gif</p> <h3><strong>7.4. calculationMode属性</strong></h3> <p>在关键帧动画中还有一个非常重要的参数,那便是calculationMode,计算模式.其主要针对的是每一帧的内容为一个座标点的情况,也就是对anchorPoint 和 position 进行的动画.当在平面座标系中有多个离散的点的时候,可以是离散的,也可以直线相连后进行插值计算,也可以使用圆滑的曲线将他们相连后进行插值计算. calculationMode目前提供如下几种模式 kCAAnimationLinear</p> <ul> <li><strong>kCAAnimationLinear :</strong> calculationMode的默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算;</li> <li><strong>kCAAnimationDiscrete</strong> 离散的,就是不进行插值计算,所有关键帧直接逐个进行显示;</li> <li><strong>kCAAnimationPaced</strong> 使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时 <strong>keyTimes和timingFunctions无效</strong> ;</li> <li><strong>kCAAnimationCubic</strong> 对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,对于曲线的形状还可以通过tensionValues,continuityValues,biasValues来进行调整自定义,这里的数学原理是Kochanek–Bartels spline,这里的主要目的是使得运行的轨迹变得圆滑;</li> <li><strong>kCAAnimationCubicPaced</strong> 看这个名字就知道和kCAAnimationCubic有一定联系,其实就是在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时 <strong>keyTimes以及timingFunctions也是无效的</strong> .</li> </ul> <p>对于这五种效果分别如下</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/c37c6e758f076bbc37fdb1b64bce8cdd.gif"></p> <p style="text-align:center">1.gif</p> <h2><strong>8. CAAnimationGroup组动画</strong></h2> <p>组动画,比较简单为两个动画的结合体</p> <pre> <code class="language-objectivec">@interface CAAnimationGroup : CAAnimation @property(nullable, copy) NSArray<CAAnimation *> *animations; @end</code></pre> <p>代码如下:</p> <pre> <code class="language-objectivec">#import "ViewController.h" @interface ViewController () @property (nonatomic,weak) CALayer *myLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. CALayer *myLayer=[[CALayer alloc] init]; self.myLayer=myLayer; myLayer.bounds=CGRectMake(0, 0, 100, 30); myLayer.position=CGPointMake(200, 200); myLayer.backgroundColor=[UIColor redColor].CGColor; [self.view.layer addSublayer:myLayer]; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ //第一组动画 CAKeyframeAnimation *keyframe=[CAKeyframeAnimation animationWithKeyPath:nil]; keyframe.keyPath=@"position"; UIBezierPath *path=[UIBezierPath bezierPathWithArcCenter:CGPointMake(200 , 200) radius:150 startAngle:0 endAngle:2*M_PI clockwise:YES]; keyframe.path=path.CGPath; //第二组 CABasicAnimation *basic=[CABasicAnimation animationWithKeyPath:nil]; basic.keyPath=@"transform.rotation"; basic.byValue=@(2*M_PI *20); //把两组动画组成组动画 CAAnimationGroup *group=[[CAAnimationGroup alloc] init]; group.duration=10; group.repeatCount=INT16_MAX; group.animations=@[keyframe,basic]; [self.myLayer addAnimation:group forKey:nil]; } @end</code></pre> <p>运行效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/fe731de2f259e40a0199148833ce2f02.gif"></p> <p style="text-align:center">组动画.gif</p> <h2><strong>9. CATransition转场动画</strong></h2> <pre> <code class="language-objectivec">/** Transition animation subclass. **/ @interface CATransition : CAAnimation @property(copy) NSString *type; @property(nullable, copy) NSString *subtype; @property float startProgress; @property float endProgress; @property(nullable, strong) id filter; @end</code></pre> <h3><strong>9.1 type动画过渡类型</strong></h3> <p>主要提供了4中过渡方式</p> <ul> <li><strong>kCATransitionFade</strong> 交叉淡化过渡(不支持过渡方向)</li> <li><strong>kCATransitionMoveIn</strong> 新视图移到旧视图上面</li> <li><strong>kCATransitionPush</strong> 新视图把旧视图推出去</li> <li><strong>kCATransitionReveal</strong> 将旧视图移开,显示下面的新视图</li> </ul> <p>私有API</p> <ul> <li><strong>cube</strong> //立方体翻滚效果</li> <li><strong>oglFlip</strong> //上下左右翻转效果</li> <li><strong>suckEffect</strong> //收缩效果,如一块布被抽走(不支持过渡方向)</li> <li><strong>rippleEffect</strong> //滴水效果(不支持过渡方向)</li> <li><strong>pageCurl</strong> //向上翻页效果</li> <li><strong>pageUnCurl</strong> //向下翻页效果</li> <li><strong>cameraIrisHollowOpen</strong> //相机镜头打开效果(不支持过渡方向)</li> <li><strong>cameraIrisHollowClose</strong> //相机镜头关上效果(不支持过渡方向)</li> </ul> <p>下面看下suckEffect效果:</p> <h2> </h2> <p style="text-align:center"><img src="https://simg.open-open.com/show/282a7c06d1fe53f54cd9896a40dd8e5a.gif"></p> <p style="text-align:center">suckEffect.gif</p> <h3><strong>9.2 subtype动画过渡方向</strong></h3> <p>规定了动画从哪个方面过来</p> <ul> <li><strong>kCATransitionFromRightl</strong></li> <li><strong>kCATransitionFromLeftl</strong></li> <li><strong>kCATransitionFromTopl</strong></li> <li><strong>kCATransitionFromBottoml</strong></li> </ul> <h3><strong>9.3 startProgress和endProgress动画起点/终点百分</strong>比</h3> <pre> <code class="language-objectivec">transition.startProgress = 0.5;</code></pre> <p>效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/d520ceb0a670d528d9e326fdbfeeaa76.gif"></p> <p style="text-align:center">startProgress = 0.5.gif</p> <p>由此可以看出:动画初始时刻已经执行了0.5,所以我们看到的是执行剩余部分的0.5部分。</p> <pre> <code class="language-objectivec">transition.endProgress = 0.8;</code></pre> <p>效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a03791b8694d54cf47daa72f6629156a.gif"></p> <p style="text-align:center">endProgress = 0.8.gif</p> <p>由此可以看出:动画在执行到0.8进度的时候,突然结束</p> <h2><strong>10. CASpringAnimation弹簧动画</strong></h2> <pre> <code class="language-objectivec">@interface CASpringAnimation : CABasicAnimation @property CGFloat mass;//目标质量 @property CGFloat stiffness;//劲度系数 @property CGFloat damping;//阻尼系数 @property CGFloat initialVelocity;//初始速度 @property(readonly) CFTimeInterval settlingDuration;//结算时间,结算时间 返回弹簧动画到停止时的估算时间,根据当前的动画参数估算 通常弹簧动画的时间使用结算时间比较准确 @end</code></pre> <p>代码示例:</p> <pre> <code class="language-objectivec">#import "ViewController.h" @interface ViewController () @property (nonatomic,strong) CALayer *testLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. CALayer *testLayer = [CALayer new]; self.testLayer = testLayer; [self.view.layer addSublayer:testLayer]; testLayer.backgroundColor = [UIColor redColor].CGColor; testLayer.frame = CGRectMake(150, 100, 100, 100); UIView *lineView1 = [[UIView alloc ] initWithFrame:CGRectMake(0, self.testLayer.position.y+200.0, [UIScreen mainScreen].bounds.size.width, 1)]; [self.view addSubview:lineView1]; lineView1.backgroundColor = [UIColor blueColor]; UIView *lineView2 = [[UIView alloc ] initWithFrame:CGRectMake(0, self.testLayer.position.y, [UIScreen mainScreen].bounds.size.width, 1)]; [self.view addSubview:lineView2]; lineView2.backgroundColor = [UIColor blackColor]; } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ CASpringAnimation *spring =[CASpringAnimation animation]; spring.keyPath = @"position.y"; spring.fromValue = @(self.testLayer.position.y); spring.toValue = @(self.testLayer.position.y+200.0); spring.mass = 5;//质量 spring.damping = 30;//阻尼系数 spring.stiffness = 300;//劲度系数 spring.initialVelocity = 0;//初始化速度 spring.duration = spring.settlingDuration;//动画执行时间 NSLog(@"settlingDuration=%lf",spring.settlingDuration); [self.testLayer addAnimation:spring forKey:nil]; } @end</code></pre> <p>运行效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/2ad67232055a7537b923fdd57b8d1927.gif"></p> <p style="text-align:center">CASpringAnimation弹簧动画.gif</p> <p> </p> <p>来自:http://www.jianshu.com/p/7b8f1e00e3bd</p> <p> </p>
本文由用户 fanny221 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
转载本站原创文章,请注明出处,并保留原始链接、图片水印。
本站是一个以用户分享为主的开源技术平台,欢迎各类分享!