iOS动画(一) - 入门基础

作者: 一剑孤城 | 来源:发表于2017-02-23 18:02 被阅读193次

    本人喜欢有趣的动效,在刚开始学习iOS开发时就自己捣腾iOS动画。当时什么也不懂,而且虽然现在看来iOS动画的实现方式就那么几种,但是刚开始弄的时候还是挺费劲的。嘿嘿,虽然现在也不算很牛,但也算入门。我来抛砖引玉了,说的不好不要打我😊。

    1.什么是动画?

    动画,电影,电视,漫画,以前傻傻的分不清,其实也好区分,很明显漫画是不会动的,比如我喜欢看的死神,火影,海贼,动画和电影神马的都是能动的,但是也有点不一样,动画是画出来的,电影则是拍出来的,呵呵哒。
    其实,我觉得一切动画都可以分解成一幅幅画面,所谓的帧动画,只要够强大,什么都挡不住一只笔的风骚。😍

    2.为何是60FPS(60帧/秒)?

    说这个问题之前,我们先说一下电影的24FPS和游戏的60FPS,一部流畅的电影只需24FPS即可,但是游戏24FPS可能会很卡。why?
    有两个原因:

    (1)电影成像和游戏成像的原理不一样

    电影每一帧都包含了一段时间的信息,而游戏则只包含那一瞬间的信息。举个例子,拍照片。如果设置曝光时间过长,则会出现模糊,因为这段时间相机里面的场景变化的痕迹都记录在照片中了,所以会出现模糊不清的照片,如下图:

    twocat.png
    一只模糊的猫头😄,其实这也可以看作一段时间内多帧糅合在一起了。电影虽然帧数低,但是上一针储存了到下一针所需要的变化,所以看起来可以连贯,就像阳光照射在人的视网膜上,会有停留时间。想象一下,站在你对面的人向你招手,速度从慢到快,你是否也不会感觉到卡顿呢?(如果能感觉到卡顿,这个世界就精彩了,想象一下奥运会的短跑项目😄)。而游戏则不一样,游戏显示在屏幕的每一帧都是先经过CPU的处理,然后再经过GPU的处理,最后才会显示在电子屏幕上面(电子屏幕显示相关的细节可以看一下ibireme大神的iOS保持界面流畅的技巧)。简单来说就是上一帧没有保留下一帧过度所需要的变化,所以,一旦前后画面变化过大,则会感觉到卡顿,因为是突然变化,措手不及。
    (2)电影的FPS是稳定的,而游戏则是不稳定的

    这个可以这样理解,电影一秒24帧,每帧之间的时间间隔是一样的,稳定的,而游戏比如前面30帧时间间隔比较短,后面30帧的时间间隔比较长,也会感觉卡顿,这也是为什么有同样的帧数,有的应用感觉卡顿,有的不卡的原因。

    总的来说,帧数越多也不一定不卡顿,但是应用显示的帧数在30~60之间一般不会感觉到明显的卡顿。貌似是因为人眼的视觉停留时间为1/24秒,具体是什么鬼,大家自行Google。

    3.iOS动画的层级结构

    层级关系.png

    解析一下上图的层级关系:
    1.UIKit应用于iOS,AppKit应用于Mac OS,可想iOS和Mac OS开发有很多相似之处。
    2.QuartzCore包含Core Animation。
    3.OpenGL ES是跨平台的图形API,属于OpenGL的一个简化版本,是应用程序编程接口,也就是说它只定义了一套规范,具体的实现由设备制造商根据规范去做。QuartZ 2D是苹果公司开发的一套API,它是Core Graphics Framework的一部分,是一套基于C的API框架,使用了Quartz作为绘图引擎。
    4.OpenGL ES可以绘制2D和3D图形,Core Graphics只能绘制2D图形。
    5.CATransform3D包含于Core Animation里面;CGAffineTransform包含于Core Graphics里面。
    6.一般从绘制性能来说:OpenGL ES > Core Graphics > Core Animation,从使用简易来说:Core Animation > Core Graphics > OpenGL ES

    4.iOS动画实现

    首先要清楚CALayer的概念,在MVC框架里面,View是负责用户界面显示的,显而易见,Apple刚开始就让iOS遵循MVC框架,UIView负责显示界面。然而,CALayer才是绘制页面的功臣,UIView只是负责管理CALayer的行为。CALayer里包含了三种树呈现树(presentationLayer tree),模型树(modelLayer tree)以及渲染树(render tree),其中呈现树主要记录了图层从动画开始到结束的所有变化,模型树只记录了最终结果,而渲染树顾名思义是渲染到屏幕上显示的。

    (1)CoreAnimation(基本动画)
    CoreAnimation.png

    由上图可见CA动画分组动画(CAAnimationGroup),属性动画(CAPropertyAnimation)和转场动画(CATransition),其中属性动画又分为基本动画(CABasicAnimation)和帧动画(CAKeyframAnimation),CA动画还实现了CAMediaTiming接口,提供一些和动画时间有关的功能。

    • CABasicAnimation
    fromValue: 初始值
    toValue: 结束值
    
    代码:
    //使用CABasicAnimation创建基础动画
    CABasicAnimation *anima = [CABasicAnimation animationWithKeyPath:@"position"];
    anima.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, SCREEN_HEIGHT/2-75)];
    anima.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-75)];
    anima.duration = 1.0f;
    //anima.fillMode = kCAFillModeForwards;
    //anima.removedOnCompletion = NO;
    [_demoView.layer addAnimation:anima forKey:@"positionAnimation"];
    注意⚠:
    如果fillMode=kCAFillModeForwards和removedOnComletion=NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。
    
    • CAKeyframeAnimation
    values : NSArray对象,里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧 
    path : 可以设置一个CGPathRef\CGMutablePathRef,让层跟着路径移动。path只对CALayer的anchorPoint和position起作用。如果你设置了path,那么values将被忽略。 
    keyTimes : 可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。当keyTimes没有设置的时候,各个关键帧的时间是平分的。
    
    代码:
    CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(SCREEN_WIDTH/2-100, SCREEN_HEIGHT/2-100, 200, 200)];
    anima.path = path.CGPath;
    anima.duration = 2.0f;
    [_demoView.layer addAnimation:anima forKey:@"pathAnimation"];
    
    • CAAnimationGroup
    animations : 用来保存一组动画对象的NSArray 
    
    代码:
    CAKeyframeAnimation *anima1 = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    NSValue *value0 = [NSValue valueWithCGPoint:CGPointMake(0, SCREEN_HEIGHT/2-50)];
    NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2-50)];
    NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2+50)];
    NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2+50)];
    NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2-50)];
    NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50)];
    anima1.values = [NSArray arrayWithObjects:value0,value1,value2,value3,value4,value5, nil];
       
    //缩放动画
    CABasicAnimation *anima2 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    anima2.fromValue = [NSNumber numberWithFloat:0.8f];
    anima2.toValue = [NSNumber numberWithFloat:2.0f];
       
    //旋转动画
    CABasicAnimation *anima3 = [CABasicAnimationanimationWithKeyPath:@"transform.rotation"];
    anima3.toValue = [NSNumber numberWithFloat:M_PI*4];
       
    //组动画
    CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
    groupAnimation.animations = [NSArray arrayWithObjects:anima1,anima2,anima3, nil];
    groupAnimation.duration = 4.0f;
       
    [_demoView.layer addAnimation:groupAnimation forKey:@"groupAnimation"];
    
    • CATransition
    CAAnimation的子类,用于做过渡动画或者转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。 
    
    type:动画过渡类型
    - kCATransitionFade 渐变效果 
    - kCATransitionMoveIn 进入覆盖效果 
    - kCATransitionPush 推出效果 
    - kCATransitionReveal 揭露离开效果 
    私有API提供了其他很多非常炫的过渡动画,比如@”cube”、@”suckEffect”、@”oglFlip”、 @”rippleEffect”、@”pageCurl”、@”pageUnCurl”、@”cameraIrisHollowOpen”、@”cameraIrisHollowClose”等。 
    注意点 
    私有api,不建议开发者们使用。因为苹果公司不提供维护,并且有可能造成你的app审核不通过。
    
    subtype:动画过渡方向
    * kCATransitionFromRight 从右侧进入
    * kCATransitionFromLeft 从左侧进入
    * kCATransitionFromTop 从顶部进入
    * kCATransitionFromBottom 从底部进入
    
    startProgress:动画起点(在整体动画的百分比) 
    endProgress:动画终点(在整体动画的百分比)
    
    代码:
    CATransition *transition = [CATransition animation];
    transition.type = kCATransitionFade;
    transition.subtype = kCATransitionFromLeft;
    transition.startProgress = 0;
    transition.endProgress = 1;
    transition.duration = 0.4;
        
    SecondViewController *svc = [[SecondViewController alloc] init];
    [self.navigationController.view.layer addAnimation: transition forKey: @"kCATransitionFade"];
    [self.navigationController pushViewController: svc animated: NO];
    

    关于部分keyPath的值:

    keyPath 描述
    transform.scale 比例变化
    transform.scale.x 宽的比例变化
    transform.scale.y 高的比例变化
    transform.rotation.z 以z轴为中心轴旋转
    opacity 透明度变化
    frame 形状变化
    path 路径变化

    5.其他调用动画的方式

    (1)UIView块动画
    _demoView.frame = CGRectMake(0, SCREEN_HEIGHT/2-50, 50, 50);
    [UIView animateWithDuration:1.0f animations:^{
        _demoView.frame = CGRectMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50, 50, 50);
    } completion:^(BOOL finished) {
        _demoView.frame = CGRectMake(SCREEN_WIDTH/2-25, SCREEN_HEIGHT/2-50, 50, 50);
    }];
    (2)UIView commit动画 //现已不使用
    _demoView.frame = CGRectMake(0, SCREEN_HEIGHT/2-50, 50, 50);
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:1.0f];
    _demoView.frame = CGRectMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50, 50, 50);
    [UIView commitAnimations];
    

    6.总结

    有的时候,app加上动画可以提高可玩性,趣味性。动画本身也挺好玩的,有兴趣的同学可以看一下我的Demo,Objective-C版Swift版

    AnimationDemo.gif

    相关文章

      网友评论

        本文标题:iOS动画(一) - 入门基础

        本文链接:https://www.haomeiwen.com/subject/yrnpwttx.html