• iOS之核心动画


    1.将动画的所有方法封装到一个类里面
    
    MyCAHelper.h
    #import <Foundation/Foundation.h>
    #import <QuartzCore/QuartzCore.h>
    
    #define kCAHelperAlphaAnimation @"opacity"; // 淡入淡出动画
    #define kCAHelperScaleAnimation @"transform.scale"; // 比例缩放动画
    #define kCAHelperRotationAnimation @"transform.rotation"; // 旋转动画
    #define kCAHelperPositionAnimation @"position"; // 平移位置动画
    
    @interface MyCAHelper : NSObject
    
    #pragma mark - 基本动画统一调用方法
    + (CABasicAnimation *)myBasicAnimationWithType:(NSString *)animationType
    duration:(CFTimeInterval)duration
    from:(NSValue *)from
    to:(NSValue *)to
    autoRevereses:(BOOL)autoRevereses;
    
    #pragma mark - 关键帧动画方法
    #pragma mark 摇晃动画
    + (CAKeyframeAnimation *)myKeyShakeAnimationWithDuration:(CFTimeInterval)duration
    angle:(CGFloat)angle
    repeatCount:(CGFloat)repeatCount;
    
    #pragma mark 贝塞尔路径动画
    + (CAKeyframeAnimation *)myKeyPathAnimationWithDuration:(CFTimeInterval)duration
    path:(UIBezierPath *)path;
    
    #pragma mark 弹力仿真动画
    + (CAKeyframeAnimation *)myKeyBounceAnimationFrom:(CGPoint)from
    to:(CGPoint)to
    duration:(CFTimeInterval)duration;
    
    @end
    MyCAHelper.m
    #import "MyCAHelper.h"
    
    @implementation MyCAHelper
    
    #pragma mark - 基本动画统一调用方法
    + (CABasicAnimation *)myBasicAnimationWithType:(NSString *)animationType
    duration:(CFTimeInterval)duration
    from:(NSValue *)from
    to:(NSValue *)to
    autoRevereses:(BOOL)autoRevereses
    {
            // 1. 实例化一个CA动画对象
            CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:animationType];
            
            // 2. 设置动画属性
            [anim setDuration:duration];
            
            [anim setFromValue:from];
            [anim setToValue:to];
            
            [anim setAutoreverses:autoRevereses];
            
            return anim;
    }
    
    #pragma mark - 关键帧动画方法
    #pragma mark 摇晃动画
    + (CAKeyframeAnimation *)myKeyShakeAnimationWithDuration:(CFTimeInterval)duration
    angle:(CGFloat)angle
    repeatCount:(CGFloat)repeatCount
    {
            // 1. 初始化动画对象实例
            CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
            
            // 2. 设置动画属性
            [anim setDuration:duration];
            
            [anim setValues:@[@(angle), @(-angle), @(angle)]];
            
            [anim setRepeatCount:repeatCount];
            
            return anim;
    }
    
    #pragma mark 贝塞尔路径动画
    + (CAKeyframeAnimation *)myKeyPathAnimationWithDuration:(CFTimeInterval)duration
    path:(UIBezierPath *)path
    {
            // 1. 初始化动画对象实例
            CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
            
            // 2. 设置动画属性
            [anim setDuration:duration];
            
            [anim setPath:path.CGPath];
            
            return anim;
    }
    
    #pragma mark 弹力仿真动画
    + (CAKeyframeAnimation *)myKeyBounceAnimationFrom:(CGPoint)from
    to:(CGPoint)to
    duration:(CFTimeInterval)duration
    {
            // 是一个基于路径的动画
            // 首先定义一个路径,记录弹力仿真的整个路径
            CGMutablePathRef path = CGPathCreateMutable();
            
            // 弹力仿真路径创建代码
            // 计算起始点与目标点之间的位置偏移量,这个偏移量的目的是为了能够计算出小球第一次延伸的长度
            CGFloat offsetX = from.x - to.x;
            CGFloat offsetY = from.y - to.y;
            
            // 1. 移动到起始点
            CGPathMoveToPoint(path, NULL, from.x, from.y);
            // 2. 将目标点的坐标添加到路径之中
            CGPathAddLineToPoint(path, NULL, to.x, to.y);
            // 3. 设置小球的弹力因子
            CGFloat offsetDivider = 4.0f;
            
            while (YES) {
                    // 加延伸方向的路径
                    CGPathAddLineToPoint(path, NULL, to.x + offsetX / offsetDivider,
                    to.y + offsetY / offsetDivider);
                    
                    // 再次将目标点添加到路径
                    CGPathAddLineToPoint(path, NULL, to.x, to.y);
                    
                    // 弹力因子递增,保证越来越接近目标点
                    offsetDivider += 6.0f;
                    
                    // 当小球的当前位置距离目标点足够小,我们退出循环
                    if ((abs(offsetX / offsetDivider) < 10.0f)
                    && (abs(offsetY / offsetDivider) < 10.0f)) {
                    
                        break;
                    }
            }
    
            CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
            [anim setPath:path];
            
            // 释放路径
            CGPathRelease(path);
            
            [anim setDuration:duration];
            
            return anim;
    }
    
    @end
    #import "ViewController.h"
    #import "MyCAHelper.h"
    
    @interface ViewController ()
    {
            UIView *_demoView;
            CGPoint location;
    }
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad
    {
            [super viewDidLoad];
            // Do any additional setup after loading the view, typically from a nib.
            [self.view setBackgroundColor:[UIColor lightGrayColor]];
            
            _demoView = [[UIView alloc]initWithFrame:CGRectMake(50, 50, 100, 100)];
            [_demoView setBackgroundColor:[UIColor whiteColor]];
            [self.view addSubview:_demoView];
    }
    
    - (void)didReceiveMemoryWarning
    {
            [super didReceiveMemoryWarning];
            // Dispose of any resources that can be recreated.
    }
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
            UITouch *touch = [touches anyObject];
            location = [touch locationInView:self.view];
            
            // [_demoView setCenter:location];
            /** 
            1.测试基本动画
            */
            // CABasicAnimation *anim = [self testBasic1];
            // [anim setRepeatCount:3];
            // 
            // [_demoView.layer addAnimation:anim forKey:nil];
            /**
            2.测试弹力仿真动画效果
            */
            // [_demoView.layer addAnimation:[self test1:_demoView.center to:location] forKey:nil];
            /**
            3.测试路径关键帧动画
            */
            // [_demoView.layer addAnimation:[self test2] forKey:nil];
            // [_demoView.layer addAnimation:[self test4:_demoView.center to:location] forKey:nil];
            /**
            4.测试摇晃关键帧动画
            */
            // 点击屏幕,开始摇晃,再次点击,停止摇晃
            // CAAnimation *anim = [_demoView.layer animationForKey:@"shakeAnimation"];
            // if (anim) {
            // [_demoView.layer removeAnimationForKey:@"shakeAnimation"];
            // } else {
            // [_demoView.layer addAnimation:[self test5] forKey:@"shakeAnimation"];
            // }
            CAKeyframeAnimation *anim = [self test1:_demoView.center to:location];
            [_demoView.layer addAnimation:anim forKey:nil];
    }
    -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
            // 需要在这里对不同对象的动画方法进行完成处理!
            [_demoView setCenter:location];
            NSLog(@"%@", NSStringFromCGPoint(_demoView.center));
    }
    
    #pragma mark - 重构方法测试
    #pragma mark 测试贝塞尔路径关键帧动画
    - (CAKeyframeAnimation *)test5
    {
            return [MyCAHelper myKeyShakeAnimationWithDuration:0.2 angle:M_PI_4 / 18 repeatCount:MAXFLOAT];
    }
    
    #pragma mark 测试贝塞尔路径关键帧动画
    - (CAKeyframeAnimation *)test4:(CGPoint)from to:(CGPoint)to
    {
            UIBezierPath *path = [UIBezierPath bezierPath];
            
            // 有两个控制点去挤出的曲线,能挤出S型的曲线
            [path moveToPoint:from];
            [path addCurveToPoint:to controlPoint1:CGPointMake(320, 0) controlPoint2:CGPointMake(0, 460)];
            
            return [MyCAHelper myKeyPathAnimationWithDuration:2.0 path:path];
    }
    
    #pragma mark 测试贝塞尔路径关键帧动画
    - (CAKeyframeAnimation *)test3:(CGPoint)from to:(CGPoint)to
    {
            UIBezierPath *path = [UIBezierPath bezierPath];
            
            // 只有一个控制点去挤出的曲线
            [path moveToPoint:from];
            [path addQuadCurveToPoint:to controlPoint:CGPointMake(320, 230)];
            
            return [MyCAHelper myKeyPathAnimationWithDuration:2.0 path:path];
    }
    
    #pragma mark 测试路径关键帧动画
    - (CAKeyframeAnimation *)test2
    {
            UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 100, 100)];
            return [MyCAHelper myKeyPathAnimationWithDuration:2.0 path:path];
    }
    
    #pragma mark 测试弹力仿真动画效果
    - (CAKeyframeAnimation *)test1:(CGPoint)from to:(CGPoint)to
    {
            CAKeyframeAnimation *anim = [MyCAHelper myKeyBounceAnimationFrom:from to:to duration:1.5];
            [anim setFillMode:kCAFillModeForwards];
            [anim setRemovedOnCompletion:NO];
            
            [anim setDelegate:self];
            
            return anim;
    }
    
    - (CABasicAnimation *)testBasic1
    {
            return [MyCAHelper myBasicAnimationWithType:@"opacity" duration:1.0 from:@(1.0) to:@(0.3) autoRevereses:YES];
    }
    
    @end

    一、核心动画概念

    -导入QuartzCore.framework框架

    1⃣ 开发步骤
    1.初始化一个动画对象(CAAnimation)并且设置一些动画相关属性

    2.CALayer中很多属性都可以通过CAAnimation实现动画效果,包括:opacity、position、transform、bounds、contents等(可以在API文档中搜索:CALayer Animatable Properties)

    3.添加动画对象到层(CALayer)中,开始执行动画

    4.通过调用CALayer的addAnimation:forKey增加动画到层(CALayer)中,这样就能触发动画。通过调用removeAnimationForKey可以停止层中的动画

    5.Core Animation的动画执行过程都是后台操作的,不会阻塞主线程

    2⃣ 属性
    1.duration:动画的持续时间

    2.repeatCount:重复次数(HUGE_VALF、MAX FLOAT无限重复)

    3.repeatDuration:重复时间(用的很少)

    4.removedOnCompletion:默认为Yes。动画执行完后默认会从图层删除掉

    5.fillMode
    6.biginTime
    7.timingFunction:速度控制函数,控制动画节奏

    8.delegate


    二、基础动画(CABasicAnimation)
    如果只是实现简单属性变化的动画效果,可以使用UIView的块动画替代基本动画

    1⃣ 属性说明
    -fromValue:keyPath相应属性值的初始值

    -toValue:keyPath相应属性的结束值

    2⃣ 动画过程说明:

    -随着动画的就行,在duration的持续时间内,keyPath相应的属性值从fromValue渐渐变为toValue

    -keyPath内容是CALayer的可动画Animation属性

    -如果fillMode=kCAFillModeForwards同时removedOnCompletion=NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态,但在实质上,图层的属性值还是动画执行前的初始值,并没有真正改变

    3⃣ 代码实现
    位移需要考虑目标点设定的问题

    三、关键帧动画(CAKeyframeAnimation)

    基础动画只能从一个值到另一个值,关键帧动画可以用一个数组保存一系列值

    1⃣ 属性说明
    -values:所有的值(用的较少)

    -path:路线(如果设置了path,那么values将被忽略)

    -keyTimes:可以为对应的关键帧制定对应的时间点,取值范围是0到1.0

    2⃣ 过程步骤
    -初始化自定义视图

    -点击屏幕,执行动画

    1.指定点平移动画(values)

    2.路径平移动画(path C语言框架CGMutablePathRef,需要手动释放内存)

    3.贝塞尔路径动画(OC框架UIBezierPath)

    4.摇晃动画(修改旋转角度)

    3⃣ 代码重构
    如上代码,重构在了一起

    四、动画组
    动画是可以并发执行的

    -定义一个group组

    -定义动画
    -将动画加入group组

    -可以给组设置属性

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
            UITouch *touch = [touches anyObject];
            CGPoint location = [touch locationInView:self.view];
            
            [_demoView setBackgroundColor:[UIColor redColor]];
            
            // 1. 定义动画组
            CAAnimationGroup *group = [CAAnimationGroup animation];
            // 定义一组动画
            // 淡入淡出动画
            CABasicAnimation *alpha = [MyCAHelper myBasicAnimationWithType:kCAHelperAlphaAnimation duration:1.0 from:@(1.0) to:@(0.3) autoRevereses:YES];
            // 旋转动画
            CABasicAnimation *rotation = [MyCAHelper myBasicAnimationWithType:kCAHelperRotationAnimation duration:2.0 from:@(-M_PI_2) to:@(M_PI_2) autoRevereses:NO];
            // 缩放动画
            CABasicAnimation *scale = [MyCAHelper myBasicAnimationWithType:kCAHelperScaleAnimation duration:0.5 from:@(1.0) to:@(0.1) autoRevereses:YES];
            
            // 关键帧路径动画,弹力仿真动画效果
            CAKeyframeAnimation *path = [self test1:_demoView.center to:location];
            
            // 2. 设置动画组属性
            [group setAnimations:@[alpha, path, rotation, scale]];
            
            // 设置动画的时长
            [group setDuration:4.0];
            
            // 3. 将动画组添加到图层
            [_demoView.layer addAnimation:group forKey:nil];
    }

    五、转场动画-CATransition
    1⃣属性说明
    -type:动画过渡类型

    -subtype:动画过渡方向

    -startProgress:动画起点(在整体动画的百分比)

    -endProgress:动画终点(在整体动画的百分比)

    -增加一个转场演示视图

    -增加轻扫手势
    -在轻扫手势方法中

    1.更改演示视图内容

    2.创建转场动画效果

    3.将转场动画添加到视图的图层

    // 轻扫手势操作
    - (void)swipeAction:(UISwipeGestureRecognizer *)sender
    {
            // 通过轻扫手势,让切换出来的视图是蓝色的
            if (_demoView.tag == 0) {
            [_demoView setBackgroundColor:[UIColor blueColor]];
            [_demoView setTag:1];
            } 
            else 
            {
            [_demoView setBackgroundColor:[UIColor redColor]];
            [_demoView setTag:0];
            }
            
            // 根据视图内容我们来实现专场动画
            CATransition *anim = [CATransition animation];
            // 设置专场动画的过渡类型
            [anim setType:@"cameraIrisHollowClose"];
            // 需要根据手势的方向,来决定专场动画的动画方向
            // 注意:在转场动画中,动画方向的左右是和手势的方向相反的
            if (sender.direction == UISwipeGestureRecognizerDirectionLeft) {
            [anim setSubtype:kCATransitionFromRight];
            } else {
            [anim setSubtype:kCATransitionFromLeft];
            }
            
            [_demoView.layer addAnimation:anim forKey:nil];
    }
    
    - (void)viewDidLoad
    {
            [super viewDidLoad];
            // Do any additional setup after loading the view, typically from a nib.
            // 1. 实例化自定义视图
            _demoView = [[UIView alloc]initWithFrame:self.view.bounds];
            [_demoView setBackgroundColor:[UIColor redColor]];
            
            [self.view addSubview:_demoView];
            
            // 2. 增加轻扫手势
            UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc]
            initWithTarget:self
            action:@selector(swipeAction:)];
            [swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
            [_demoView addGestureRecognizer:swipeLeft];
            
            UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc]
            initWithTarget:self
            action:@selector(swipeAction:)];
            [swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
            [_demoView addGestureRecognizer:swipeRight];
    }

    六、UIView的转场动画-双视图

    +(void)transitionWithView:(UIView*)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void(^)(void))animations completion:(void(^)(BOOL finished))completion;
    
    1⃣参数说明
    -duration:动画的持续时间
    
    -view:需要进行转场动画的视图
    
    -options:转场动画的类型
    
    -animations:将改变视图属性的代码放在这个Block里
    
    -completion:动画结束后,自动调用的Block
    
    @interface ViewController ()
    {
            UIImageView *_demoImageView;
            UIImageView *_demoImageView2;
    }
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad
    {
            [super viewDidLoad];
            // Do any additional setup after loading the view, typically from a nib.
            // 实例化第一个UIImageView的对象
            _demoImageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"1.jpg"]];
            [self.view addSubview:_demoImageView];
            
            // 实例化第二个UIImageView对象
            // 注意:在双视图转场动画中,不要将第二个视图添加到主视图
            _demoImageView2 = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"2.jpg"]];
    }
    
    - (void)didReceiveMemoryWarning
    {
            [super didReceiveMemoryWarning];
            // Dispose of any resources that can be recreated.
    }
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
            // 单击屏幕的时候,实现转场动画效果
            [self animation1];
    }
    
    #pragma mark - 单视图的转场动画
    - (void)animation2
    {
            UIImageView *from;
            UIImageView *to;
            
            if (_demoImageView.superview) {
            from = _demoImageView;
            to = _demoImageView2;
            } 
            else 
            {
            from = _demoImageView2;
            to = _demoImageView;
            }
            
            [UIView transitionFromView:from toView:to duration:1.0f options:UIViewAnimationOptionTransitionCrossDissolve completion:^(BOOL finished) {
            NSLog(@"image view1 的主视图: %@", _demoImageView.superview);
            NSLog(@"image view2 的主视图: %@", _demoImageView2.superview);
            }];
    }
    
    #pragma mark - 单视图的转场动画
    - (void)animation1
    {
            [UIView transitionWithView:_demoImageView duration:1.0f options:UIViewAnimationOptionTransitionCurlUp animations:^{
            // 在动画块代码中设置视图内容变化
                    if (_demoImageView.tag == 0) {
                            [_demoImageView setImage:[UIImage imageNamed:@"2.jpg"]];
                            [_demoImageView setTag:1];
                    } 
                    else 
                    {
                            [_demoImageView setImage:[UIImage imageNamed:@"1.jpg"]];
                            [_demoImageView setTag:0];
                    }
            } completion:nil];
    }
  • 相关阅读:
    Vue3使用vue3-video-player
    centos搭建phantomjs
    windows与Linux写入后门
    webService静态调用方法
    Web开发学习笔记(日更)
    docker修改容器绑定端口
    linux下~/.bashrc、/etc/profile、 $PATH环境变量 作用和修改
    docker for windows安装,修改images位置,修改镜像源,查看/var/lib/docker/containers
    hive修改表DDL
    python 高性能异步爬虫线程&线程池
  • 原文地址:https://www.cnblogs.com/xujinzhong/p/8436240.html
Copyright © 2020-2023  润新知