有什么能让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难以实现。
等等,这里只是手指滑动的时候要做的事情,当手指松开后如何让控制点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来监听位置变化了下面是单个值的改变
现在我只需要在touchesEnd方法中调用这个方法就可以达到同样的效果了!