谈谈iOS中粘性动画以及果冻效果的实现
ntrolPoint1:c3 controlPoint2:c4]; [ovalPath addCurveToPoint:pointD controlPoint1:c5 controlPoint2:c6]; [ovalPath addCurveToPoint:pointA controlPoint1:c7 controlPoint2:c8]; [ovalPath closePath]; CGContextAddPath(ctx, ovalPath.CGPath); CGContextSetFillColorWithColor(ctx, self.indicatorColor.CGColor); CGContextFillPath(ctx); }
现在,当你滑动ScrollView的时候,小球就会形变了。
如何用阻尼振动函数创建出60个关键帧?
上面的例子中,有个很重要的因素,就是ScrollView中的contentOffset.x这个变量,没有这个输入,那接下来什么都不会发生。但想要获得这个变量,是需要用户触摸、滑动去交互产生的。在某个动画中用户是没有直接的交互输入的,比如当手指离开之后,要让这个小球以果冻效果弹回初始状态,这个过程手指已经离开屏幕,也就没有了输入,那么用上面的方法肯定行不通,所以,我们可以用CAAnimation.
我们知道,iOS7中苹果在 UIView(UIViewAnimationWithBlocks) 加入了一个新的制作弹性动画的工厂方法:
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);
但是没有直接的关于弹性的 CAAnimation 子类,类似CABasicAnimation或CAKeyframeAnimation 来直接给CALayer添加动画。好消息是iOS9中添加了公开的 CASpringAnimation。但是出于兼容低版本以及对知识探求的角度,我们可以了解一下如何手动给CALayer创建一个弹性动画。
在开始之前需要复习一下高中物理知识 ———— 阻尼振动,你可以点击高亮字体的链接稍微复习一下。
根据维基百科,我们可以得到如下振动函数通式:
当然这只是一个通式,我们需要让 图像过(0,0),并且最后衰减到1 。我们可以让原图像先绕X轴翻转180度,也就是加一个负号。然后沿y轴向上平移一个单位。所以稍加变形可以得到如下函数:
想看函数的图像?没问题,推荐一个在线查看函数图象的网站 —— Desmos ,把这段公式 1-\left(e^{-5x}\cdot \cos (30x)\right) 复制粘帖进去就可以看到图像。
改进后的函数图像是这样的:
完美满足了我们 图形过(0,0),震荡衰减到1 的要求。其中式子中的 5 相当于阻尼系数,数值越小幅度越大;式子中的 30 相当于震荡频率 ,数值越大震荡次数越多。
接下来就需要转换成代码。
总体思路是创建60帧关键帧(因为屏幕的最高刷新频率就是60FPS),然后把这60帧数据赋值给 CAKeyframeAnimation 的 values 属性。
用以下代码生成60帧后保存到一个数组并返回它,其中//1就是利用刚才的公式创建60个数值:
+(NSMutableArray *) animationValues:(id)fromValue toValue:(id)toValue usingSpringWithDamping:(CGFloat)damping initialSpringVelocity:(CGFloat)velocity duration:(CGFloat)duration{ //60个关键帧 NSInteger numOfPoints = duration * 60; NSMutableArray *values = [NSMutableArray arrayWithCapacity:numOfPoints]; for (NSInteger i = 0; i numOfPoints; i++) { [values addObject:@(0.0)]; } //差值 CGFloat d_value = [toValue floatValue] - [fromValue floatValue]; for (NSInteger point = 0; point CGFloat x = (CGFloat)point / (CGFloat)numOfPoints; CGFloat value = [toValue floatValue] - d_value * (pow(M_E, -damping * x) * cos(velocity * x)); //1 y = 1-e^{-5x} * cos(30x) values[point] = @(value); } return values; }
接下来创建一个对外的类方法,并返回一个 CAKeyframeAnimation :
+(CAKeyframeAnimation *)createSpring:(NSString *)keypath duration:(CFTimeInterval)duration usingSpringWithDamping:(CGFloat)damping initialSpringVelocity:(CGFloat)velocity fromValue:(id)fromValue toValue:(id)toValue{ CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:keypath]; NSMutableArray *values = [KYSpringLayerAnimation animationValues:fromValue toValue:toValue usingSpringWithDamping:damping * dampingFactor initialSpringVelocity:velocity * velocityFactor duration:duration]; anim.v
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)