CAAnimation - 1: CABasicAnimation
隐式动画
_myView = [[UIView alloc] initWithFrame:CGRectMake(50, 100, 50, 50)];
_myView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:_myView];
_myLayer = [CALayer layer];
_myLayer.frame = CGRectMake(50, 200, 50, 50);
_myLayer.backgroundColor = [UIColor purpleColor].CGColor;
[self.view.layer addSublayer:_myLayer];
_myView.alpha = 0.2; // 不存在过渡动画
_myLayer.opacity = 0.2; // 存在过渡动画
- UIView 的 alpha 和 CALayer 的 opacity 应该是一致的, opacity 会和 alpha 同步变化
Layer 树
- Render Tree (Private): 渲染 // 不讨论
- Model Layer Tree: [layer modelLayer]: Layer 属性的最终值
- Presentation Layer Tree: [layer presentationLayer]: Layer 在某一个时间点的近似值
layer 运动过程, layer.position.x 初始为0
假若 layer 向右平移 100pt, layer.position.x, 所以变化如下
((CALayer *)[layer modelLayer]).position.x = 100 // 直接到达100
((CALayer *)[layer presentationLayer]).position.x = 0 ... 100 // 在运动过程中缓慢增加到100
CATransaction
- 对 layer 属性的改变, 在渲染动画之前, 系统会产生一个 Transaction 事务, 打包所有动画操作, 然后提交给 RenderTree
- CATransaction 各个属性
+ (void)begin;
// 所有操作应该放在这两个方法之中
+ (void)commit;
+ (void)setAnimationDuration:(CFTimeInterval)dur; // 设置默认的时间值
+ (void)disableActions; // 禁止隐式动画
+ (void)setCompletionBlock:(nullable void(^)(void))block; // 完成动画之后的回调
[CATransaction begin];
[CATransaction setCompletionBlock:^{
NSLog(@"Set position animation completed.");
}];
[CATransaction setDisableActions:YES]; // 禁用隐式动画
[CATransaction setAnimationDuration:1]; // 动画时间
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
_myLayer.position = CGPointMake(_myLayer.position.x + 100, _myLayer.position.y);
[CATransaction commit];
- 显式事务: 放在 begin 和 commit 中间的操作, 显式声明了事务的过程
- 隐式事务: 不声明事务过程, 但系统在操作过程中会自动添加事务, 比如说默认的动画时长 0.25s
显式动画
CABasicAnimation 属性
哪个属性动
/* CABasicAnimation class */
// 指定哪个属性动, keyPath 是一个字符串
// "opacity" 透明度; "position" 位置; "position.x" 位置的 x 值; ......
// [CABasicAnimation animationWithKeyPath:@"position.x"]; 创建一个 BasicAnimation 对象
+ (instancetype)animationWithKeyPath:(nullable NSString *)path;
@property (nullable, copy) NSString *keyPath;
怎么动
// 怎么动
// 这三个值必须是一致的类型
// 三个属性可以为空, 但是不能设置超过两个属性值
@property (nullable, strong) id fromValue;
@property (nullable, strong) id toValue;
@property (nullable, strong) id byValue;
// 只设置以下值会发生的情况
fromValue && toValue // fromValue ~ toValue
fromValue && byValue // fromValue ~ (fromValue + byValue)
toValue && byValue // (toValue - byValue) ~ toValue
fromValue // fromValue ~ PresentationLayer.value (fromValue ~ currentValue)
toValue // PresentationLayer.value ~ toValue (currentValue ~ toValue)
byValue // PresentationLayer.value ~ PresentationLayer.value + byValue (currentValue ~ currentValue + byValue)
谁动
// 给 layer 添加动画
- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;
// 获取动画的名字
- (nullable NSArray<NSString *> *)animationKeys;
// 获取指定名字的动画对象
- (nullable CAAnimation *)animationForKey:(NSString *)key;
CABasicAnimation 动画demo
- 动画过程中, 改变的是 PresentationLayer 的值, 但 modelLayer 的值不会被改变. 动画结束的时候, 动画被移除, 所以位置会恢复到 modelLayer 的值, 即还原到原来的位置
-
addAnimation:forKey:
这个过程中, add 进去的 animation 会被拷贝的, 所以添加完动画之后, 对动画进行修改将不生效
CAMediaTiming
@protocol CAMediaTiming
@property CFTimeInterval duration; // 虽然默认是0, 但是还是会保持动画隐式动画默认的0.25s
@property float repeatCount; // 动画的重复次数, 设置 HUGE_VALF 可以设置成无限次
@property CFTimeInterval repeatDuration; // 每一次动画持续的时间, 和 repeatCount 不能同时设置, 系统通过计算总时间和每次动画时长计算重复的次数
@property BOOL autoreverses; // 回复
@end
动画结束之后如何不返回原来的位置
- 在动画结束的时候, 再进行一次位置的设置操作, 将 modelLayer 的值设置成位移之后应该变成的值
- 在改变 layer 的位置的时候, 会产生隐式动画, 导致两个动画的产生
- 保持两个动画的 key 值一致, 后面添加的动画会覆盖前面添加的动画
- 所以, 可以通过设置一致的动画 key 值, 将显式动画覆盖隐式动画
// ------ animation ------
layer.position.x, byValue = 100;
// ------ animation stop ------
layer.position.x += 100;
CoreAnimation Timing
- CAMediaTiming System
-
CACurrentMediaTime()
获取创建的 CALayer 在系统时间轴上对应的时间是什么
- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(nullable CALayer *)l;
- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(nullable CALayer *)l;
@protocol CAMediaTiming
@property CFTimeInterval beginTime; // 动画开始的时间,
// animation.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] + 1; 表示 layer 动画添加之后的一秒执行
@property float speed; // 让整个动画加速,
// 设置动画时长 duration 为10, 默认 speed 为1; 当 speed 设置成2的时候, 动画将在5秒结束, 当 speed 设置成5的时候, 动画将在2秒结束, 以此类推
@property CFTimeInterval timeOffset; // 相当于把动画的运动作为一个循环, 设置 timeOffset 属性, 将会从 offset 的时间点进行动画, 结束之后循环到动画开始的时候进行运动
// 相对于整个动画的时长进行的 offset, 不会被 speed 影响
@end
暂停动画
- 动画过程中, 调用暂停方法, 动画暂停在调用时的状态
timeOffset = 0 && speed = suitableValue;
- 不能通过修改
animation.speed = 0 && animation.timeOffset = xxx;
进行设置
- 因为 animation 添加到 Layer 上之后, 就被复制了, 之后再修改 animation 的属性不会对动画产生影响
- 所以要使用
layer.speed = 0 && layer.timeOffset = xxx;
lt = (tp - begin) * speed + offset
// lt: layer local time
// tp: parent time
// begin: default 0
// speed: default 1
// offset: default 0
// 所以: lt = (tp - 0) * 1 + 0 => lt = tp
// lt = [layer convertTime:CACurrentMedaiTime() fromLayer:nil];
// tp = (lt - offset) / speed + begin
- 动画过程
- time: tp0; speed=1, begin=0, lt=lt0, offset=0, => tp0 = lt0; // 动画开始
- time: tp1; speed=0, begin=0, lt=lt1, offset=0, => tp1 = lt1; // 动画暂停
- 将 speed 置为0, => lt1' = offset = 0
- 因为 lt1' 为0, 所以动画会恢复到原来的位置. 要保存动画位置状态, 需要让 lt1' = lt1, 即 offset = lt1
- time: tp2; speed=1, begin=0, lt=lt2, offset=0, => tp2 = lt2;
CAMediaTiming 动画结束后保持
@property (copy) NSString *fillMode;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
animation.fromValue = (id)[UIColor yellowColor].CGColor; // 从黄色变化到橙色
animation.toValue = (id)[UIColor orangeColor].CGColor;
animation.duration = 2;
animation.fillMode = kCAFillModeForwards; // 动画结束之后保存动画状态, 需要将 layer 动画设置为不移除
animation.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nir] + 1; // 延迟一秒开始动画
[layer addAnimation:animation forKey:@"backgroundColor"];
CAMediaTiming - timingFunction
+ (instancetype)functionWithName:(NSString *)name;
+ (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
CAAnimation - 2:
CASpringAnimation
@interface CASpringAnimation : CABasicAnimation
@property CGFloat mass; // 质量, 弹簧物品的质量越大, 惯性越大
@property CGFloat stiffness; // 刚度, 相当于拉弹簧的力
@property CGFloat damping; // 阻尼; 取值范围 >= 0
@property CGFloat initialVelocity; // 初始速度
@property (readonly) CFTimeInterval settlingDuration; // 弹簧预估的时间
CAKeyFrameAnimation
@interface CAKeyFrameAnimation : CAPropertyAnimation
@property (nullable, copy) NSArray *values; // 进行动画的值 包含第一帧和最后一帧
@property (nullable, copy) NSArray<NSNumber *> *keyTimes; // 分别动画的时长 包含第一帧和最后一帧
@property (nullable, copy) NSArray<CAMediaTimingFunction *> *timingFunctions; // 动画速率
-
calculationMode
对动画运动路径产生变化
// 下面三个参数用于定制 calculateMode 路径
@property (nullable, copy) NSArray<NSNumber *> *tensionValues;
@property (nullable, copy) NSArray<NSNumber *> *continuityValues;
@property (nullable, copy) NSArray<NSNumber *> *biasValues;
-
additive
对动画参数进行叠加, 即在原来动画的基础上增量增加动画
-
path
可以覆盖 values 属性
- path 在被赋值的时候是通过复制完成的
- 需要将 calculateMode 设置为 kCAAnimationPaced
-
rotationMode
在进行动画的时候自动进行旋转
CATransition
-
type
描述动画的形式
cube
oglFlip
- ...
// CoreAnimation 2-3 06:00
-
subtype
动画的方向
-
startProgress
整个动画起始时间
endProgress
-
filter
定制动画的效果
CALayer 实现翻转的动画
CATransition *animation = [CATransition animation];
animation.duration = 1;
animation.type = @"oglFlip";
animation.subtype = kCATransitionFromLeft;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[parentView.layer addAnimation:animation forKey:@"myTrans"];
// 在动画中, 动画的 key 值都会被设置为 kCATransition
subview1.hidden = !subview1.hidden;
subview2.hidden = !subview2.hidden;
CAAnimationGroup
-
animations
动画组合
- timingFunction 会叠加
- 如果要设置 animation1 的 beginTime, 可以直接设置, 因为 animation1 属于 group 动画, group 是从0开始的
- 动画的 duration 超出了 group 的 duration, 则会被截断
- speed 和 timingFunction 都是会叠加的
CAAnimationGroup *group = [CAAnimationGroup animation];
group.duration = 2;
group.animations = @[animation1, animation2];
group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
group.fillMode = kCAFillModeForward;
group.removedOnCompletion = NO;
[layer addAnimation:group forKey:@"group"];
特殊的 Layers
CAShapeLayer
shapeLayer
fillColor
-
path
这个 path 不支持隐式动画
stroke; line
// path 动画 圆形和正方形进行转换
UIBezierPath *path1 = [UIBezierPath bezierPathWithOvalInRect:layer.bounds];
UIBezierPath *path2 = [UIBezierPath bezierPathWithRect:layer.bounds];
layer.path = path1.CGPath;
- (void)touchesEnded {
CGPathRef fromValue = layer.path;
CGPathRef toValue = CGPathEqualToPath(path1.CGPath, fromValue) ? path2.CGPath : path1.CGPath;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.fromValue = (__bridge id _Nullable)(fromValue);
animation.toValue = (__bridge id _Nullable)(toValue);
animation.duration = 2;
layer.path = toValue;
[layer addAnimation:animation forKey:animation.keyPath];
}
-
fillColor
填充颜色
-
fillRule
填充规则
-
lineWidth
线的宽度
-
lineCap
线头形状
-
lineJoin
连接时
miterLimit
-
lineDashPhase
虚线从哪里开始画
-
lineDashPattern
虚线的样式 @[@10, @10]: 实线部分10, 虚线部分10
利用 CAShapeLayer 实线一个进度视图
CAReplicatorLayer
instanceCount
instanceDelay
instanceTransform
-
instanceColor
instanceRedOffset
instanceGreenOffset
instanceBlueOffset
instanceAlphaOffset
CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
replicatorLayer.frame = CGRectMake(0, 200, self.view.bounds.size.width, 50);
[self.view.layer addSublayer:replicatorLayer];
CALayer *layer = [CALayer layer];
layer.backgroundColor = [UIColor blueColor].CGColor;
layer.frame = CGRectMake(0, 0, 20, 50);
[replicatorLayer addSublayer:layer];
replicatorLayer.instanceCount = 10; // 复制10次
replicatorLayer.instanceTransform = CATransform3DMakeTranslation(20, 0, 0);
CAEmitterLayer
-
CAEmitterLayer
粒子发射器
Emitter
CAEmitterCell
网友评论