美文网首页收藏iosiOS点点滴滴
iOS 弹性动画解析与封装

iOS 弹性动画解析与封装

作者: 4335151e8554 | 来源:发表于2017-12-22 18:16 被阅读806次
    QQ20171221-161056-HD.gif

    有什么能让App界面变得更加生动呢,答案就是动画效果,其中带弹性的动画效果我尤其喜欢,iOS 给我们提供了一个api
    +(void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);
    但是这个api有一个局限性,就是他只能控制一个view的整体变化,像上图那种只让矩形的底边做弹性运动,光靠这个api难以实现。


    QQ20171221-164020.png 以上动画的实现思路其实也是现在网上比较流行的一种思路:红色区域是一个CAShapeLayer,通过不断的改变CAShapeLayer的path来改变其形状。在touchBegin方法中获得当前触摸点,生成路径赋值给shapeLayer,现在CE边就可以跟着手指动啦。 QQ20171222-171251.png 问题的关键就是不断的改变这个path来限制CAShapeLayer的轮廓。变化的地方就是C点到E点这条路径,其中D点是CE边的控制点,通过改变D点坐标,不断的生成新的path赋值给CAShapeLayer,这样CE边就会跟着手指不断变化,CAShapeLayer的形状也就相应的变化了。

    等等,这里只是手指滑动的时候要做的事情,当手指松开后如何让控制点D动态地回到原处(即CE的中心位置)并且还要有弹性效果呢?这里就需要用到上面所说的弹性动画api了,其实我在D点这个位置放了一个view,就是动图上面的黑点,手指滑动的时候黑色view也跟着D点运动,当手指松开时对黑色view做一个弹性动画,并且运行一个CADisplayLink,并且在CADisplayLink触发的方法中不断获取黑色view.layer.presentationLayer.position的值,刷新D点的位置,这样D点也能跟着黑色view做弹性运动了,这样就实现了回弹和弹性效果。
    是不是很麻烦!又是加黑色view,又是对黑色view做震荡动画,又是开CADisplayLink,又是监控黑色view的位置,其实就是为了获得一串带震荡效果的点,并连续的赋值给D点。所以这里我提供一个封装了的类,里面有一个方法就是产生连续的带震荡效果的值,并且不断的将值赋值给block,这样外界就能在block中直接获得这些值做动画了。我在这个类里面已经开了CADisplayLink,会不断的调用block,并且将新值付给block。
    .h文件

    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    typedef void(^CallBackBlockValue)(CGFloat value);
    typedef void(^CallBackBlockPoint)(CGPoint point);
    @interface animationTools : NSObject
    //单个值的震荡
    -(void)animationWithFormValue:(CGFloat)formValue
                          toValue:(CGFloat)toValue
                          damping:(CGFloat)damping
                         velocity:(CGFloat)velocity
                         duration:(CGFloat)duration
                         callback:(CallBackBlockValue)callback;
    
    //点的震荡
    -(void)animationWithFormPoint:(CGPoint)formPoint
                          toPoint:(CGPoint)toPoint
                          damping:(CGFloat)damping
                         velocity:(CGFloat)velocity
                         duration:(CGFloat)duration
                         callback:(CallBackBlockPoint)callback;
    @end
    

    .m文件

    #import "animationTools.h"
    #import <UIKit/UIKit.h>
    
    @interface animationTools()
    @property (nonatomic,strong) CADisplayLink *displaylink;
    @property (nonatomic,assign) CGFloat formValue;
    @property (nonatomic,assign) CGFloat toValue;
    @property (nonatomic,assign) CGPoint formPoint;
    @property (nonatomic,assign) CGPoint toPoint;
    @property (nonatomic,assign) CGFloat damping;
    @property (nonatomic,assign) CGFloat velocity;
    @property (nonatomic,assign) NSInteger numberOfnum;
    @property (nonatomic,assign) NSInteger beginNum;
    @property (nonatomic,copy)   CallBackBlockValue callbackValue;
    @property (nonatomic,copy)   CallBackBlockPoint callbackPoint;
    @end
    @implementation animationTools
    
    
    /*
     * damping = 5;     //越小 幅度越大
     * velocity = 30;   //越大 震动次数越多
     */
    -(void)animationWithFormValue:(CGFloat)formValue
                          toValue:(CGFloat)toValue
                          damping:(CGFloat)damping
                         velocity:(CGFloat)velocity
                         duration:(CGFloat)duration
                         callback:(CallBackBlockValue)callback
    {
        [self.displaylink invalidate];
        self.displaylink = nil;
        self.numberOfnum = duration * 60;   //总帧数
        self.formValue = formValue;
        self.toValue = toValue;
        self.damping = damping;
        self.velocity = velocity;
        self.beginNum = 0;
        self.callbackValue = callback;
        self.displaylink = [CADisplayLink displayLinkWithTarget:self selector:@selector(SEL_Displaylink_value)];
        [self.displaylink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    }
    
    -(void)SEL_Displaylink_value
    {
        if (self.beginNum >= self.numberOfnum) {
            [self.displaylink invalidate];
            self.displaylink = nil;
        }
        //计算
        CGFloat i = self.beginNum * 1.0/self.numberOfnum;
        CGFloat value = self.toValue - (self.toValue - self.formValue) * (pow(M_E, -self.damping * i) * cos(self.velocity * i)); //1 y = 1-e^{-5x} * cos(30x)
        if (self.callbackValue != nil) {
            self.callbackValue(value);
        }
        self.beginNum ++;
    }
    
    -(void)animationWithFormPoint:(CGPoint)formPoint
                          toPoint:(CGPoint)toPoint
                          damping:(CGFloat)damping
                         velocity:(CGFloat)velocity
                         duration:(CGFloat)duration
                         callback:(CallBackBlockPoint)callback
    {
        [self.displaylink invalidate];
        self.displaylink = nil;
        self.numberOfnum = duration * 60;   //总帧数
        self.formPoint = formPoint;
        self.toPoint = toPoint;
        self.damping = damping;
        self.velocity = velocity;
        self.beginNum = 0;
        self.callbackPoint = callback;
        self.displaylink = [CADisplayLink displayLinkWithTarget:self selector:@selector(SEL_Displaylink_Point)];
        [self.displaylink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    }
    
    -(void)SEL_Displaylink_Point
    {
        if (self.beginNum >= self.numberOfnum) {
            [self.displaylink invalidate];
            self.displaylink = nil;
        }
        //计算
        CGFloat i = self.beginNum * 1.0/self.numberOfnum;
        CGFloat x = self.toPoint.x - (self.toPoint.x - self.formPoint.x) * (pow(M_E, -self.damping * i) * cos(self.velocity * i)); //1 y = 1-e^{-5x} * cos(30x)
        CGFloat y = self.toPoint.y - (self.toPoint.y - self.formPoint.y) * (pow(M_E, -self.damping * i) * cos(self.velocity * i));
        CGPoint point = CGPointMake(x, y);
        if (self.callbackPoint != nil) {
            self.callbackPoint(point);
        }
        self.beginNum ++;
    }
    
    @end
    

    需要注意的是damping参数和duration参数,damping数值越小震动次数越多,可能duration设置的比较小,在duration时间结束后弹性效果还没结束,导致骤然停止。需要在使用的时候调试。

    使用很简单!创建对象调用这个对象方法,告诉他起点和终点等信息,他就会不断的刷新point值并调用block,以后就不用加辅助view来监听位置变化了 QQ20171222-165241.png 效果图gif质量太低了。。。 QQ20171222-164555-HD.gif
    下面是单个值的改变 QQ20171222-173235.png QQ20171222-173347-HD.gif 以下是打印出来的部分值,可以看出value在100上下震动,并最终趋于100 QQ20171222-173645.png
    现在我只需要在touchesEnd方法中调用这个方法就可以达到同样的效果了! QQ20171222-180922.png 效果真的一样,甚至要更好,只是这个gif质量太差了。。 QQ20171222-181117-HD.gif

    相关文章

      网友评论

        本文标题:iOS 弹性动画解析与封装

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