动画| 金币抛入红包动画详解
2018-03-19 本文已影响27人
進无尽
前言
这个动画效果很早就出来了,也是一个比较经典的关键帧动画和组合动画的运用,通过剖析源码,可以发现实际上这个酷炫的动画实现起来很简单。
金币.gif
实现过程
- 在当前页面加载一个福袋的图片和再来一次的按钮。
- 在for 循环中使用延迟调用函数。每个函数的调用时间越来越靠后,达到依次出现的效果。
- 在每个延迟调用函数中创建一个金币的图片,并记录它的tag和最终的位置。
- 为这个金币图片随机生成开始位置,并根据开始位置和结束位置计算出控制点,利用这三点绘制二次贝塞尔曲线。
- 每个金币图层都执行一个动画组,一边沿轨迹做抛物线动画一遍做从大到小的3D缩放动画。
- 每个金币执行完动画后,从图层中移除。
- 所有金币都执行完动画后钱袋图层执行摇晃动画。
立即打开
//统计金币数量的变量
static int coinCount = 0;
- (void)getCoinAction:(UIButton *)btn
{
//"立即打开"按钮从视图上移除
[btn removeFromSuperview];
//初始化金币生成的数量
coinCount = 0;
for (int i = 0; i<kCoinCountKey; i++) {
//延迟调用函数
[self performSelector:@selector(initCoinViewWithInt:) withObject:[NSNumber numberWithInt:i] afterDelay:i * 0.01];
}
}
延迟调用函数
- (void)initCoinViewWithInt:(NSNumber *)i
{
UIImageView *coin = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"icon_coin_%d.png",[i intValue] % 2 + 1]]];
//初始化金币的最终位置
coin.center = CGPointMake(CGRectGetMidX(self.view.frame) + arc4random()%40 * (arc4random() %3 - 1) - 20, CGRectGetMidY(self.view.frame) - 20);
coin.tag = [i intValue] + 1;
NSLog(@"KKK: %lf",CGRectGetMidX(self.view.frame));
//每生产一个金币,就把该金币对应的tag加入到数组中,用于判断当金币结束动画时和福袋交换层次关系,并从视图上移除
[_coinTagsArr addObject:[NSNumber numberWithInteger:coin.tag]];
[self.view addSubview:coin];
[self setAnimationWithLayer:coin];
}
每个金币图层都需要做的动画组
- (void)setAnimationWithLayer:(UIView *)coin
{
CGFloat duration = 1.6f;
////////////////////////////////////////////////////////////////////////////////////////////
//绘制从底部到福袋口之间的抛物线
CGFloat positionX = coin.layer.position.x; //终点x
CGFloat positionY = coin.layer.position.y; //终点y
int fromX = arc4random() % 320; //起始位置:x轴上随机生成一个位置
int height = [UIScreen mainScreen].bounds.size.height + coin.frame.size.height; //y轴以屏幕高度为准
int fromY = arc4random() % (int)positionY; //起始位置:生成位于福袋上方的随机一个y坐标
CGFloat cpx = positionX + (fromX - positionX)/2; //x控制点
CGFloat cpy = fromY / 2 - positionY; //y控制点,确保抛向的最大高度在屏幕内,并且在福袋上方(负数)
CGMutablePathRef path = CGPathCreateMutable();
//动画的起始位置
CGPathMoveToPoint(path, NULL, fromX, height);
CGPathAddQuadCurveToPoint(path, NULL, cpx, cpy, positionX, positionY);
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
[animation setPath:path];
CFRelease(path);
path = nil;
////////////////////////////////////////////////////////////////////////////////////////////
//图像由大到小的变化动画
CGFloat from3DScale = 1 + arc4random() % 10 *0.1;
CGFloat to3DScale = from3DScale * 0.5;
CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
scaleAnimation.values = @[[NSValue valueWithCATransform3D:CATransform3DMakeScale(from3DScale, from3DScale, from3DScale)], [NSValue valueWithCATransform3D:CATransform3DMakeScale(to3DScale, to3DScale, to3DScale)]];
scaleAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
////////////////////////////////////////////////////////////////////////////////////////////
//动画组合
CAAnimationGroup *group = [CAAnimationGroup animation];
group.delegate = self;
group.duration = duration;
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.animations = @[scaleAnimation, animation];
[coin.layer addAnimation:group forKey:@"position and transform"];
}
每个金币图层动画组执行完后执行
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
if (flag) {
//动画完成后把金币和数组对应位置上的tag移除
UIView *coinView = (UIView *)[self.view viewWithTag:[[_coinTagsArr firstObject] intValue]];
[coinView removeFromSuperview];
[_coinTagsArr removeObjectAtIndex:0];
//全部金币完成动画后执行的动作
if (++coinCount == kCoinCountKey) {
[self bagShakeAnimation];
if (_getBtn) {
[self.view addSubview:_getBtn];
[_getBtn setTitle:@"再来一次" forState:UIControlStateNormal];
}
}
}
}
福袋晃动动画
- (void)bagShakeAnimation
{
CABasicAnimation* shake = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
shake.fromValue = [NSNumber numberWithFloat:- 0.2];
shake.toValue = [NSNumber numberWithFloat:+ 0.2];
shake.duration = 0.1;
shake.autoreverses = YES;
shake.repeatCount = 4;
[_bagView.layer addAnimation:shake forKey:@"bagShakeAnimation"];
}
PS:如果我们把每一个金币图层的路径都画出来的话,会更明白图层的轨迹了: