iOS滚珠菜单动效

代码实现效果 gitHub地址:https://github.com/BearRan/FlowMenuAnimation

这里写图片描述

原图效果
http://www.tuyiyi.com/v/46575.html
原图效果


前言:

注意!由于collisionBoundsType是iOS9.0之后才有的,所以运行本demo时一定要是9.0之后的系统,不然会出现异常。而且屏幕适配暂时没调,所以iPhone6S运行最佳。

本来一味着想用CAAnimation来实现,却发现CABasicAnimation和CAKeyFrameAnimation完全不够用。有一位网友提醒我用有物理效果的UIDynamic这个试试,但是之前没有接触过这一块,也就只能现学现卖了。

本章只说明滚珠的难点和动效实现,其他部分的动效就简单的带过了。关于UIDynamic如何使用本文也不做讲解,文章结尾会放置一些比较好的链接。

难点:

  1. 滚珠路径如何绘制
  2. 滚珠动画该分解成几个步骤
  3. 如何分解滚珠的物理效果

###难点解析:
####1.滚珠路径如何绘制

使用BezierPath是毋庸置疑的,但是如何勾勒出来呢?用代码一点一点地调太费时间了。建议使用Sketch和CodePaint共同完成。
dmg资源:http://blog.youkuaiyun.com/xiongbaoxr/article/details/50989283
如何使用可以参考这篇博客:http://blog.youkuaiyun.com/xiongbaoxr/article/details/50890565
Sketch:可以方便地勾勒出路径,并且生成SVG文件。
CodePaint:可以将SVG的路径文件转换成代码

大致步骤如下:

  1. 把效果图截取一张,拖到Sketch做背景

  2. 新建图层,使用钢笔工具临摹背景图勾勒出路径

  3. 注意:勾勒时可以选择非镜像的控制点,这样方便调整曲线

这里写图片描述

接着点击Sketch右下角的倒出按钮,格式选为SVG格式

这里写图片描述

将生成的SVG文件拖入到codePaint中,拖入成功后会直接看到如下界面

这里写图片描述

看到代码路径后可以整段copy出来,当然,我建议把所有的点抽离出来,方便做适配

代码绘制凹槽动画

demo的AppDelegate.h中有以下这些开关,可以自己调配,方便观察

这里写图片描述

单个滚珠动效演示,这样看应该比较容易理解。其实我是盖了一个新的图层专门用来做滚珠动效的。
这里写图片描述

控制点就是用普通的UIAniamtion实现的,在动画之行的过程中,通过CADisplayLink实时观察Point的PresentLayer的position来不停的重绘贝塞尔曲线。

####2.滚珠动画该分解成几个步骤

主要分为以下步骤
阶段一:滚珠一起滚落下来时到最高点的阶段;
阶段二:滚珠滚落到最高点后回流的的阶段;
阶段三:滚珠消失阶段;

####3.如何分解滚珠的物理效果

UIDynamic有以下物理效果

  • UIGravityBehavior:重力行为
  • UICollisionBehavior:碰撞行为
  • UISnapBehavior:捕捉行为
  • UIPushBehavior:推动行为
  • UIAttachmentBehavior:附着行为
  • UIDynamicItemBehavior:动力元素行为

###UIGravityBehavior:重力行为
不再解释

#pragma mark  重力行为
- (UIGravityBehavior *)addGravityBehavior:(id <UIDynamicItem>)item
{
    UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] init];
    [gravityBehavior addItem:item];
    [_animator addBehavior:gravityBehavior];
    
    return gravityBehavior;
}

###UICollisionBehavior:碰撞行为
适用于:UIView和父类view的边界碰撞,以及和其他UIView碰撞
这里可以配合重力行为来设定小球的运动路径

#pragma mark  碰撞行为
- (UICollisionBehavior *)addCollisionBehavior:(id <UIDynamicItem>)item
{
    UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] init];
    [collisionBehavior addItem:item];
    [collisionBehavior addBoundaryWithIdentifier:@"path" forPath:_beizerPath];
    [_animator addBehavior:collisionBehavior];
    
    return collisionBehavior;
}

###UISnapBehavior:捕捉行为
顾名思义,不解释

UIPushBehavior:推动行为
使用瞬间或持续的力并按照某一方向作用于某个UIView
用于开始或结束时的小球推动

#pragma mark  显现动画,第一个球向右的推力
- (UIPushBehavior *)addPushBehavior_inFirstBtn
{
    UIButton *tempBtn = _btnArray[0];
    UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[tempBtn] mode:UIPushBehaviorModeInstantaneous];
    pushBehavior.pushDirection = CGVectorMake(1, 0.3);
    pushBehavior.magnitude = 1.6;
    [_animator addBehavior:pushBehavior];
    
    return pushBehavior;
}

###UIAttachmentBehavior:附着行为
UIView和某个UIView的相互吸附行为
此处用于小球见的相互吸附作用

#pragma mark  添加球与球之间的附着行为
- (UIAttachmentBehavior *)addAttachmentBehavior_item:(id <UIDynamicItem>)item attachToItem:(id <UIDynamicItem>)attachToItem
{
    SpecialBtn *tempBtn = (SpecialBtn *)item;
    
    UIAttachmentBehavior *attachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:item attachedToItem:attachToItem];
    [attachmentBehavior setLength:tempBtn.width + 20];
    [attachmentBehavior setDamping:10.01];
    [attachmentBehavior setFrequency:1];
    [_animator addBehavior:attachmentBehavior];
    
    return attachmentBehavior;
}

###UIDynamicItemBehavior:动力元素行为
一些其他的物理元素,比如摩擦力,线速度阻力,角速度阻力等

#pragma mark  动力元素行为
- (UIDynamicItemBehavior *)addDynamicItemBehavior:(id <UIDynamicItem>)item
{
    UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[item]];
    itemBehavior.resistance = 0;
    itemBehavior.allowsRotation = YES;
    itemBehavior.angularResistance = 4.0;
    itemBehavior.friction = 0.8;
    [_animator addBehavior:itemBehavior];
    
    return itemBehavior;
}

###阶段一:滚珠一起滚落下来时到最高点的阶段
####球与球之间的附着行为 + 重力行为+碰撞行为+ 动力元素行为+ 第一个球向右的推力

#pragma mark 显现动画
- (void)showBtnsAnimation
{
    _animatorStatus = kAnimatorStatus_open;
    [_animator removeAllBehaviors];
    
    if (showPath) {
        _pathLayer.path = _beizerPath.CGPath;
        _pathLayer.fillColor = [UIColor clearColor].CGColor;
        _pathLayer.strokeColor = [UIColor orangeColor].CGColor;
        _pathLayer.lineWidth = 2.0;
        [self.layer addSublayer:_pathLayer];
    }
    
    CGFloat btn_gap = [self setXX:16];
    for (int i = 0; i < [_btnArray count]; i++) {
        
        SpecialBtn *tempBtn = _btnArray[i];
        tempBtn.tag = i;
        [self addSubview:tempBtn];
        
        //  设定初始位置
        [tempBtn setX:(tempBtn.width + btn_gap) * ([_btnArray count] - 1 - i) + btn_gap];
        [tempBtn setY:-tempBtn.height];
        
        //  添加球与球之间的附着行为
        if (i > 0) {
            [self addAttachmentBehavior_item:_btnArray[i] attachToItem:_btnArray[i - 1]];
        }
        
        //  重力行为
        UIGravityBehavior *gravityBehavior = [self addGravityBehavior:tempBtn];
        if (i == [_btnArray count] - 1) {
            //  最后一个球处理重力行为
            [self dealLastBtnGravityBehavior:gravityBehavior tempBtn:tempBtn];
        }

        //  碰撞行为
        [self addCollisionBehavior:tempBtn];
        
        //  动力元素行为
        UIDynamicItemBehavior *itemBehavior = [self addDynamicItemBehavior:tempBtn];
        if (i == [_btnArray count] - 1) {
            //  最后一个球增加密度,以防止出现的时候,由于惯性的原因导致飞起来
            itemBehavior.density = 1.8;
        }
        
    }
    
    //  第一个球向右的推力
    [self addPushBehavior_inFirstBtn];
}

###阶段二:滚珠滚落到最高点后回流的的阶段
####球与球之间的附着行为 + 重力行为+碰撞行为+ 动力元素行为+ 最后一个球向左push

for (int i = 0; i < [_btnArray count]; i++) {
                    
                    //  添加球与球之间的附着行为
                    if (i > 0) {
                        
                        UIAttachmentBehavior *attachmentBehavior = [self addAttachmentBehavior_item:_btnArray[i] attachToItem:_btnArray[i - 1]];
                        [attachmentBehavior setFrequency:5];
                        [attachmentBehavior setLength:tempBtn.width + 5];
                    }
                    
                    //  重力行为
                    [self addGravityBehavior:_btnArray[i]];
                    
                    //  碰撞行为
                    [self addCollisionBehavior:_btnArray[i]];
                    
                    //  动力元素行为
                    UIDynamicItemBehavior *itemBehavior = [self addDynamicItemBehavior:_btnArray[i]];
                    itemBehavior.angularResistance = 15.0;
                }
                
                //  最后一个球向左push
                UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[tempBtn] mode:UIPushBehaviorModeContinuous];
                pushBehavior.pushDirection = CGVectorMake(-1, -0.5);
                pushBehavior.magnitude = 2.3;
                [_animator addBehavior:pushBehavior];

###阶段三:滚珠消失阶段
####球与球之间的附着行为 + 重力行为+碰撞行为+ 动力元素行为+ 最后一个球向左push(和阶段二几本一致,只是推动的力大了一些)

#pragma mark 消退动画
- (void)closeBtnsAniamtion
{
    NSLog(@"-- closeBtnsAniamtion");
    
    _animatorStatus = kAnimatorStatus_close;
    SpecialBtn *lastBtn = (SpecialBtn *)[_btnArray lastObject];
    
    [_animator removeAllBehaviors];
    
    for (int i = 0; i < [_btnArray count]; i++) {
        
        //  添加球与球之间的附着行为
        if (i > 0) {
            
            UIAttachmentBehavior *attachmentBehavior = [self addAttachmentBehavior_item:_btnArray[i] attachToItem:_btnArray[i - 1]];
            [attachmentBehavior setFrequency:5];
            [attachmentBehavior setLength:lastBtn.width + 5];
        }
        
        //  重力行为
        UIGravityBehavior *gravityBehavior = [self addGravityBehavior:_btnArray[i]];
        if (i == 0) {
            [self dealFirstDisappearBtnGravityBehavior:gravityBehavior tempBtn:_btnArray[i]];
        }
        
        //  碰撞行为
        [self addCollisionBehavior:_btnArray[i]];
        
        //  动力元素行为
        [self addDynamicItemBehavior:_btnArray[i]];
    }
    
    //  最后一个球向左push
    UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[lastBtn] mode:UIPushBehaviorModeContinuous];
    pushBehavior.pushDirection = CGVectorMake(-1, -0.5);
    pushBehavior.magnitude = 10.0;
    [_animator addBehavior:pushBehavior];
}

结束语:

以上即是滚珠动画的核心步骤和讲解。

###UIDynamic资料相关网址:

Core Animation详解(三)-UIDynamic Animation
http://www.th7.cn/Program/IOS/201502/389463.shtml
WWDC 2013 Session笔记 - UIKit Dynamics入门
https://onevcat.com/2013/06/uikit-dynamics-started/
iOS动画——DynamicAnimate 力学动画
http://www.cnblogs.com/madpanda/p/4742563.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值