iOS 弹性动画解析与封装

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,455评论 6 30
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,089评论 5 13
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,370评论 25 707
  • 目录 下一章 人生如戏,全靠演技 京师 汉王府 朱远坦早早的就迎在了门口,见朱远圻骑马而来,露出了笑容,往前走了...
    横横丨横阅读 276评论 0 4
  • 以前打工的时候,做市场,做研发,做营销基本都是在一个边界内,惯性的去满足已有的需求就不会出错。 但创业不同,一切要...
    逄格亮阅读 237评论 0 0