老规矩,效果图如下所示:

  

ios动画水泡上升 水泡动画图片_ios动画水泡上升

  刚开始我看这个动画的时候真的被震撼到了,无论用什么来实现都觉得非常非常得难,用粒子发射器,动力框架,感觉都做不出来.只觉得其中的算法肯定及其复杂又复杂, 然而,越是高级的程序员越是能用"懒惰的方法"实现复杂的功能,没错,对于这个动画处理方式,我只是封装了一个imageView的子类而已,并没有想象当中的那么复杂,我把动画实现的渲染图放到下面,你看了也绝对会恍然大悟:

  

ios动画水泡上升 水泡动画图片_封装_02

  是否瞬间觉得这个动画简单了许多,我以前好像提到过,看似高级的动画,其实基本都是由几个CoreAnimation的叠加形成的,接下来我们便分析一下这个动画是怎么叠加做成的?

  分解:

  先不要看那么多的气泡,但从一个气泡上面进行分析:

  1.从渲染图上可以看出,一个气泡上面用了大量的随机函数,气泡的大小,气泡的透明度,贝塞尔曲线的变化,所以封装的过程当中必然要先写一个随机的方法,方便调用;

  2.贝塞尔曲线变化的规律,从渲染图可以看出,贝塞尔曲线的起点是不变的,变的只是有三个点,左边的控制点,和有右边的控制点,以及贝塞尔曲线的终点;

  3.让气泡沿着贝塞尔曲线行走,用关键帧动画是最理想不过的了,关键帧动画不仅能够制定它的key数组,还能给他制定一条路径,让气泡沿着这条路径行走

  4.气泡走到贝塞尔曲线终点的时候,我们也可以看到它有一个爆破的效果,因此关键帧动画必然有一个delegate方便在动画结束的时候执行爆破动画

  5.动画爆破的效果主要有两个动作  放大->消失  放大之后还要消失,所以用UIView封装的transitionWithView来做这个效果在得知放大之后(动画结束之后),将泡泡imageView从父视图中移除掉;

  单个泡泡的分析完毕,那么一群泡泡的分析就简单多了,就只有一个特点,随机在屏幕的中间X轴的位置上产生一个新的泡泡!

  分析完毕之后就可以写代码了,整体的代码封装在一个集成了UIImageView的BubbleImageView上面

  首先是随机取一个从 min 到 max的一个浮点值函数的写法  

- (CGFloat)makeRandomNumberFromMin:(CGFloat)min toMax: (CGFloat)max
{
NSInteger precision = 100;
    
CGFloatABS(max - min);
    
    subtraction *= precision;
    
CGFloatarc4random() % ((int)subtraction+1);
    
    randomNumber /= precision;
    
    randomNumber += min;
    
//返回结果
return randomNumber;
}
 
以下是BubbleImageView的属性列表:
@interfaceBubbleImageView ()
 
@propertynonatomic, assign)CGFloat//贝塞尔曲线的最大高度
@propertynonatomic, assign)CGFloat//贝塞尔曲线的最大宽度
 
@propertynonatomic, assign)BubblePathType pathType;
 
//每一次动画执行的最大高度和最大宽度
@propertynonatomic, assign)CGFloat nowMaxHeight;
 
@propertynonatomic, assign)CGFloat nowMaxWidth;
 
@propertynonatomic, assign)CGPoint originPoint;
 
@propertynonatomic, assign)CGRect originFrame;
 
//贝塞尔曲线的渲染layer;
//@property (nonatomic, strong)CAShapeLayer *shapeLayer;
 
@end
 接着是BubbleImageView的初始化方法
 
- (instancetype)initWithMaxHeight:(CGFloat) maxHeight maxWidth: (CGFloat)maxWidth maxFrame:(CGRect)frame andSuperView: (UIView *)superView
{
selfBubbleImageViewalloc]initWithImage:[UIImageimageWithContentsOfFile:[[NSBundlemainBundle] pathForResource:@"bubble"ofType:@"png"]]];
ifself) {
        
self.originFrame = frame;
self.frameselfgetRandomFrameWithFrame:frame];
        
self.originPointself.frame.origin;
addSubview:self];
self.maxHeight = maxHeight;
        
        
self.maxWidth = maxWidth;
        
self.alphaselfmakeRandomNumberFromMin:0.5 toMax:1];
        
selfgetRandomBubbleType];
        
selfgetRandomPathWidthAndHeight];
        
selfsetUpBezierPath];
        
    }
    
returnself;
    
}
//三个随机函数的方法如下面所示
 
//这个随机函数是为了获得,泡泡是先往哪个方向走,总共有两种方向,第一种是先左后右,第二种是先右后左
//如手稿所示

ios动画水泡上升 水泡动画图片_关键帧动画_03

 

- (void)getRandomBubbleType
{
ifarc4random() %2 ==1) {
self.pathTypeBubblePathTypeLeft;
else{
self.pathTypeBubblePathTypeRight;
    }
}
 
 
- (void)getRandomPathWidthAndHeight {
self.nowMaxHeightselfmakeRandomNumberFromMin:self.maxHeighttoMax:self.maxHeight];
self.nowMaxWidthselfmakeRandomNumberFromMin:0 toMax:self.maxWidth];
}
 
 
 
- (CGRect)getRandomFrameWithFrame: (CGRect)frame
{
 
CGFloatselfmakeRandomNumberFromMin:15 toMax:self.originFrame.size.width];
CGRectCGRectMake(frame.origin.x, frame.origin.y, width , width);
return randomFrame;
    
    
}
 
 
//绘制贝塞尔曲线方法
 
//这里绘制贝塞尔曲线的方法需要重点讲一下:
大致原理图如下手稿所示

ios动画水泡上升 水泡动画图片_泡泡动画_04

 

- (void)setUpBezierPath
{
//开始绘制泡泡的贝塞尔曲线
UIBezierPathUIBezierPathbezierPath];
moveToPoint:self.originPoint];
    
    
ifself.pathTypeBubblePathTypeLeft) {
CGPointCGPointMake(self.originPoint.xself.nowMaxWidthself.originPoint.yself.nowMaxHeight / 4);
CGPointCGPointMake(self.originPoint.xself.nowMaxWidthself.originPoint.yself.nowMaxHeight *3 / 4);
CGPointCGPointMake(self.originPoint.x, self.originPoint.yself.nowMaxHeight);
        
addCurveToPoint:termalPoint controlPoint1:leftControllPoint controlPoint2:rightControllPoint];
else{
CGPointCGPointMake(self.originPoint.xself.nowMaxWidthself.originPoint.yself.nowMaxHeight * 3 / 4);
CGPointCGPointMake(self.originPoint.xself.nowMaxWidthself.originPoint.yself.nowMaxHeight / 4);
CGPointCGPointMake(self.originPoint.x, self.originPoint.yself.nowMaxHeight);
        
        
addCurveToPoint:termalPoint controlPoint1:rightControllPoint controlPoint2:leftControllPoint];
        
    }
    
//    self.shapeLayer = [CAShapeLayer layer];
//    self.shapeLayer .path = bezierPath.CGPath;
//    [self.shapeLayer  setStrokeColor:[UIColor redColor].CGColor];
//    [self.superview.layer addSublayer:self.shapeLayer ];
    
CAKeyframeAnimationCAKeyframeAnimationanimationWithKeyPath:@"position"];
setDuration:2.0];
pathCGPath;
fillModekCAFillModeForwards;
removedOnCompletionNO;
delegateself;
self.layeraddAnimation:keyFrameAnimation forKey:@"movingAnimation"];
}
//绘制完贝塞尔曲线和添加玩动画之后,我们在动画停止之后的那个方法进行爆破动画
 
- (void)animationDidStop:(CAAnimationBOOL)flag
{
CATransactionbegin];
CATransactionsetCompletionBlock:^{
// transform the image to be 1.3 sizes larger to
// give the impression that it is popping
UIViewtransitionWithView:self
duration:0.1f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
self.transformCGAffineTransformMakeScale(1.3, 1.3);
completion:^(BOOL finished) {ifYES) {
self.layerremoveAllAnimations];
BubbleImageViewBubbleImageViewalloc]initWithMaxHeight:self.maxHeightmaxWidth:self.maxWidthmaxFrame:self.originFrameandSuperView:self.superview];
//                                [self.shapeLayer removeFromSuperlayer];
                                
selfremoveFromSuperview];
                            }
                           
 
                        }];
    }];
CATransactioncommit];}
//完成一个泡泡的封装之后,你若将它直接添加到view上面去将会看到这样的效果

ios动画水泡上升 水泡动画图片_ios动画水泡上升_05

//接下来我们来将一下蓝色的渐变色是怎么制作的,需要用到一个CALayer的子类CAGradientLayer,只要将它的colors数组赋值,便能够实现渐变的效果,当然你也可以指定它的startPoint和endPoint默认是渐变方向从上往下,所以这里并没有指定
CAGradientLayerCAGradientLayeralloc]init];
setFrame:CGRectMake(0, [UIScreenmainScreen].bounds.size.heightself.view.bounds.size.width, self.view.bounds.size.height / 2)];
colors__bridgeid)[UIColorwhiteColor].CGColor,(__bridgeid)[UIColorcolorWithRed:0 green:0.5 blue:1 alpha:0.5].CGColor,(__bridgeid)[UIColorblueColor].CGColor];
self.view.layeraddSublayer:gradientLayer];

//好了,我们的泡泡动画到这里就结束了,代码的设计上并不是很难,关键在相关参数的调试上,如果你需要Demo的话可以到我的GitHub上面下载

//下载地址是:

https://github.com/bnb173yjx/BubbleAnimationDemo