一,iOS动画概念
绚丽动画效果是IOS系统的一大特点,通过UIView层封装的动画,基本可以满足应用开发的大部分需求,但如果想要满足自由的控制动画的展示,就需要使用CoreAnimation框架中的一些类和方法!下图就很好的展示了UIKit与CoreAnimation的关系!如下图:
1, Layer和view的关系与区别
UIView通过内部图层layer显示在屏幕上,本身并不能显示。当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘图,并且会将所有内容绘制在自己的layer图层上,绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示,如图:
Layer是专门用来辅助我们绘制图像的层,通过每个坐标点与矩阵的运算,来决定最后绘制的状态!而view是要用来接受事件和处理用户交互的。然而,每一个view中,都有一个layer属性来辅助我们进行图形的绘制,并且Layer是可以层级嵌套的。
2, view的2D形变
UIView有个transform的属性,通过设置该属性,我们可以实现调整该view在其superView中的大小和位置,具体来说,Transform(变化矩阵)是一种3×3的矩阵,通过这个矩阵我们可以对一个坐标系统进行缩放,平移,旋转以及这两者的任意组着操作。而且矩阵的操作不具备交换律,即矩阵的操作的顺序不同会导致不同的结果。
struct CGAffineTransform {
CGFloat a, b, c, d;
CGFloat tx, ty;
};
//原本默认的设置
self.containerView.transform = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
//添加简单的动画
[UIView animateWithDuration:1.0f animations:^{
self.containerView.transform = CGAffineTransformMakeRotation(-30);
}];
系统已经给出了方便的api调用
//平移
CGAffineTransformMakeTranslation(CGFloat tx,CGFloat ty)
//缩放
CGAffineTransformMakeScale(CGFloat sx,CGFloat sy)
//旋转
CGAffineTransformMakeRotation(CGFloat angle) (angle 旋转的角度)
//恢复
CGAffineTransformInvert(CGAffineTransform t)
案例:user1
3, layer的3D形变
CALayer同样也有一个transform属性,但它的类型是CATransform3D,而不是CGAffineTransform。CALayer对应于UIView的transform属性叫做affineTransform.CATransform3D也是一个矩阵,但是和2x3的矩阵不同,CATransform3D是一个可以在3维空间内做变换的4x4的矩阵. ,如图
struct CATransform3D
{
CGFloatm11(x缩放),m12(y切变),m13(旋转),m14();
CGFloatm21(x切变),m22(y缩放),m23(),m24();
CGFloatm31(旋转),m32( ),m33(),m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义);
CGFloatm41(x平移),m42(y平移),m43(z平移),m44();
};
4, 正投影与透视投影
m34:管理z方向的深度,m34 = -1/z中,当z为正的时候,是我们人眼观察现实世界的效果,即在投影平面上表现出近大远小的效果,z越靠近原点则这种效果越明显,越远离原点则越来越不明显,当z为正无穷大的时候,则失去了近大远小的效果,此时投影线垂直于投影平面!
案例:user2
绕任意一个轴旋转公式:
)
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)
二, 图层及其相关子类
案例讲解:
1,显示动画与隐式动画
CATransaction:
CATransaction是事务,用于批量提交多个对layer-tree的操作,并且是原子性的。所有对layer-tree的修改都必须包含在事务内。事务可以嵌套。
隐式CATransaction:
所有对layer-tree的操作都必须处于事务。修改UIView的属性最终也是修改到了layer-tree。当我们改动到layer-tree时,如果当前没有显式创建过CATransaction,则系统会创建一个隐式的CATransaction,这个隐式CATransaction会在RunLoop结束后commit。
另外事务可以嵌套,当事务嵌套时候,只有最外层的事务commit了之后,整个动画才会执行。
案例CATransaction
2,锚点
锚点:锚点决定了图层的绘制位置以及在动画被展示时其参照的点,瞄点的取值范围为0~1(Layer层的position参照点始终和和锚点重合)(决定了旋转,放大,缩小的参照点)(anchorPoint设置锚点)如图:
案例anchorPoint
3,CAEmitterLayer
用于控制粒子效果,例如,钱包雨,烟花,火焰等效果
案例:FFEmitterDemo
4,CTransformLayer
用于渲染3D Layer 层次结构,就像在其子view上可以实现layer任意的旋转
案例:user3
5,CAGradientLayer
用于控制颜色渐变,项目里经常用到
案例:CAGradient
三,核心动画
在CoreAnimation中,动画效果都是添加在图层的变化上的,通过CALayer,我们组织复杂的层级结构。例如:改变图层的大小,颜色,圆角等。layer层并不决定试图的展示,它只是存储了试图的几何状态。
1,转场动画(CATransition)
CATransition类实现层的转场动画。你可以从一组预定义的转换或者通过提供定制的CIFilter实例来指定转场效果。
案例:CATransition
例子:
UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
UIView *yellowView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
yellowView.backgroundColor = [UIColor yellowColor];
[self.view addSubview:yellowView];
UIButton *button2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button2 setTitle:@"改变2" forState:UIControlStateNormal];
button2.frame = CGRectMake(10, 500, 300, 40);
[button2 addTarget:self action:@selector(changeUIView2) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button2];
- (void)changeUIView2
{
CATransition *transition = [CATransition animation];
transition.delegate = self;
transition.duration = 1.5f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
//@"cube" @"moveIn" @"reveal" @"fade"(default) @"pageCurl" @"pageUnCurl" @"suckEffect" @"rippleEffect" @"oglFlip"
transition.type = @"rippleEffect";//另一种设置动画效果方法
transition.subtype = kCATransitionFromLeft;
[self.view exchangeSubviewAtIndex:1 withSubviewAtIndex:0];
[self.view.layer addAnimation:transition forKey:@"animation"];
}
2,CABasicAnimation && CAAnimationGroup
CABasicAnimation 为layer属性提供了基础的帧动画能力。
CAAnimationGroup是可以保存一组动画对象,将 CAAnimationGroup对象加入图层后,组中所有动画对象可以同时并发运行。
案例:CABasicAnimation
CABasicAnimation *positionAnima = [CABasicAnimation animationWithKeyPath:@"position.y"];
positionAnima.fromValue = @(self.containerView.center.y);
positionAnima.toValue = @(self.containerView.center.y-100);
positionAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
transformAnima.fromValue = @(0);
transformAnima.toValue = @(3 * M_PI);
transformAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
CAAnimationGroup *animaGroup = [CAAnimationGroup animation];
animaGroup.duration = 4;
animaGroup.fillMode = kCAFillModeForwards;
///为了让呈现树的图形不移除
animaGroup.removedOnCompletion = NO;
animaGroup.fillMode = kCAFillModeForwards;
animaGroup.repeatCount = 1;
animaGroup.animations = @[positionAnima,transformAnima];
[self.containerView.layer addAnimation:animaGroup forKey:@"Animation"];
3,CASpringAnimation
iOS 9 新出的CASpringAnimation,是苹果专门解决开发者关于弹簧动画的这个需求而封装的类。
案例:CASpringAnimation
- (void)springAnimationTextAction:(CGPoint)point {
CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:@"bounds"];
//路径计算模式 (@"position")
if ([springAnimation.keyPath isEqualToString:@"position"]) {
springAnimation.fromValue = [NSValue valueWithCGPoint:self.containerView.layer.position];
springAnimation.toValue = [NSValue valueWithCGPoint:point];
}else if ([springAnimation.keyPath isEqualToString:@"position.x"]) {
springAnimation.fromValue = @(self.containerView.layer.position.x);
springAnimation.toValue = @(point.x);
}else if ([springAnimation.keyPath isEqualToString:@"position.y"]) {
springAnimation.fromValue = @(self.containerView.layer.position.y);
springAnimation.toValue = @(point.y);
}else if ([springAnimation.keyPath isEqualToString:@"bounds"]) {
springAnimation.fromValue = [NSValue valueWithCGRect:CGRectMake(point.x, point.y, 500, 500)];
springAnimation.toValue = [NSValue valueWithCGRect:self.containerView.frame];
}
//质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大 Defaults to one
springAnimation.mass = 5;
//刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快 Defaults to 100
springAnimation.stiffness = 100;
//阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快 Defaults to 10
springAnimation.damping = 3;
//初始速率,动画视图的初始速度大小 Defaults to zero
//速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反
springAnimation.initialVelocity = 10;
//估算时间 返回弹簧动画到停止时的估算时间,根据当前的动画参数估算
NSLog(@"====%f",springAnimation.settlingDuration);
springAnimation.duration = springAnimation.settlingDuration;
//removedOnCompletion 默认为YES 为YES时,动画结束后,恢复到原来状态
springAnimation.removedOnCompletion = NO;
[self.containerView.layer addAnimation:springAnimation forKey:@"springAnimation"];
}
4,CAKeyFrameAnimation
任何动画要表现运动或变化,至少前后要给出两个不同的关键状态,而中间状态的变化和衔接电脑可以自动完成,在Flash中,表示关键状态的帧动画叫做关键帧动画
案例:CAKeyFrameAnimation
CALayer *layer = [CALayer layer];
layer.bounds = CGRectMake(0, 0, 120, 120);
//与基础动画不同,关键帧动画必须指明动画初始值
layer.position = CGPointMake(100, 300);
layer.cornerRadius = 60;
layer.masksToBounds = YES;
layer.contents = (id)[UIImage imageNamed:@"timg"].CGImage;
[self.view.layer addSublayer:layer];
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CALayer *layer = [self.view.layer.sublayers lastObject];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//设置关键帧
//与基础动画不同,关键帧动画必须指明动画初始值
NSValue *value1 = [NSValue valueWithCGPoint:layer.position];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(50, 300)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(50, 100)];
animation.duration = 2;
animation.values = @[value1,value2,value3];
animation.autoreverses = YES;
[layer addAnimation:animation forKey:nil];
}
四,参考:
1,https://www.jianshu.com/p/469a406db8a3
2,https://www.jianshu.com/p/941fc7105c7a
3,https://blog.csdn.net/ssirreplaceable/article/details/52979399
4,https://www.cnblogs.com/LifeTechnologySupporter/p/10353073.html