美文网首页
CADisplayLink+UIBezierPath实现镂空动画

CADisplayLink+UIBezierPath实现镂空动画

作者: Lucky_C | 来源:发表于2020-06-06 10:57 被阅读0次

    前奏

    最近接到一个新的需求,需要一个自定义相机的圆形按钮并且实现中间镂空的动画,效果如下

    image

    大概是一开始想得太简单了,实现过程中尝试了几种方式都发现不能完美实现效果,最后在折腾了半天后采用CADisplayLink+UIBezierPath总算实现了

    实现

    首先是申明的属性

    ///遮罩layer
    @property (nonatomic, strong) CAShapeLayer * maskLayer;
    ///按钮视图
    @property (nonatomic, strong) UIView * hollowV;
    /// 按钮状态打开/关闭
    @property (nonatomic, assign) BOOL isOpen;
    /// 动画计时器
    @property (nonatomic, strong) CADisplayLink * timer;
    /// 记录动画开始时间
    @property (nonatomic, assign) double tempLinkStamp;
    /// 动画进度 0 - 1
    @property (nonatomic, assign) CGFloat progress;
    

    在viewDidLoad中创建视图

    UIView * view = [[UIView alloc] initWithFrame:CGRectMake((Screen_Width - 100) / 2, Screen_Height - 300, 100, 100)];
    view.backgroundColor = UIColor.whiteColor;
    _hollowV = view;
    

    接下来就是在_hollow中挖出我们的需要的镂空样式

    这里实现原理是使用UIBezierPath绘制出我们需要的描边,然后通过为视图设置layer遮罩实现镂空的效果,具体如下

    ///根据半径绘制UIBezierPath
    ///radius为要挖去部分的半径
    - (UIBezierPath *)pathWithHollowRadius:(CGFloat)radius {
        //取视图边框路径
        UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(_hollowV.frame.size.width / 2, _hollowV.frame.size.height / 2) radius:_hollowV.frame.size.width / 2 startAngle:0 endAngle:2 * M_PI clockwise:NO];
        //要挖去的部分
        UIBezierPath * hollowPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(_hollowV.frame.size.width / 2, _hollowV.frame.size.height / 2) radius:radius startAngle:0 endAngle:2 * M_PI clockwise:YES];
        [path appendPath:hollowPath];
       
        return path;
    }
    

    使用绘制的的UIBezierPath设置遮罩layer

    //镂空遮罩layer
        _maskLayer = [CAShapeLayer layer];
        _maskLayer.path = [self pathWithHollowRadius: _hollowV.frame.size.width / 2 - 4].CGPath;
        _hollowV.layer.mask = _maskLayer;
    

    于是乎我们就得到了这样一个镂空


    Simulator Screen Shot - iPhone 11 Pro Max - 2020-06-05 at 22.18.13.png

    动画实现

    首先之前有未完成的动画就直接停掉并直接设置到完成的状态

    progress:镂空打开的进度,打开动画时progress 是 0 -> 1,关闭是 1 -> 0

    /// 打开动画
    - (void)openAni {
        if (_timer) {
            [self stopTimer];
            self.progress = 0;
        }
        _isOpen = YES;
        //清空之前的记录,重新开始动画
        _tempLinkStamp = 0;
        _timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(onlink:)];
        [_timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    ///关闭动画
    - (void)closeAni {
        if (_timer) {
            [self stopTimer];
            self.progress = 1;
        }
        _isOpen = NO;
        //清空
        _tempLinkStamp = 0;
        _timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(onlink:)];
        [_timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    }
    

    CADisplayLink的回调处理。因为CADisplayLink的频率是和屏幕每一帧刷新同步的,所以动画实现后的效果灰常丝滑

    - (void)onlink:(CADisplayLink *)link {
        if (_tempLinkStamp == 0) {
            _tempLinkStamp = link.timestamp;
        }
        
        //动画时间
        CGFloat animationTime = 0.5;
        //计算动画执行时间
        CGFloat time = link.timestamp - _tempLinkStamp;
        //以0.5秒为动画时间,计算当前动画进度
        CGFloat progress = _isOpen ? time / animationTime : 1 - time / animationTime;
        //更新视图
        self.progress = progress;
        
        if (time >= 0.5) {
            [self stopTimer];
        }
    }
    
    - (void)stopTimer {
        [_timer invalidate];
        _timer = nil;
    }
    

    最后根据计时器回调中计算的进度更新动画效果

    ///更新,根据镂空的进度,更新视图
    -(void)setProgress:(CGFloat)progress {
        _progress = progress;
        
        NSLog(@"%f",progress);
        
        //镂空layer
        CAShapeLayer * layer = _hollowV.layer.mask;
        //
        CGFloat radius = (_hollowV.frame.size.width / 2 - 4) * progress;
        layer.path = [self pathWithHollowRadius:radius].CGPath;
       
    }
    

    添加一个点击测试

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        if (_isOpen) {
            [self closeAni];
        } else {
            [self openAni];
        }
    }
    

    到这里就完成了!

    总结

    通过CADisplayLink实现的动画,在一定程度上可以有更自由的发挥空间,而且性能甚至更好

    相关文章

      网友评论

          本文标题:CADisplayLink+UIBezierPath实现镂空动画

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