iOS实现爆炸动画效果

//
//  UIView+animation.m
//  BoomAnimation
//
//  Copyright © 2019年 van. All rights reserved.
//

#import "UIView+animation.h"
#import <objc/runtime.h>
#include<stdio.h>

#define square 65

@implementation UIView (animation)

- (void)boomWithTileSize:(CGSize)size inDiameter:(CGFloat)diameter {
    
    UIGraphicsBeginImageContext(self.bounds.size);
    [[UIColor clearColor] setFill];
    [[UIBezierPath bezierPathWithRect:self.bounds] fill];
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    [self.layer renderInContext:ctx];
    
    UIImage *screenshotImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    UIView* animationView = [[UIView alloc] initWithFrame:self.frame];
    [self.superview addSubview:animationView];
    objc_setAssociatedObject(self, @"boomAnimationView", animationView, OBJC_ASSOCIATION_RETAIN);
    
    NSInteger maxX = floor(screenshotImage.size.width / size.width);
    NSInteger maxY = floor(screenshotImage.size.height / size.height);
    for (int i=0; i<maxX; i++) {
        for (int j = 0; j<maxY; j++) {
            @autoreleasepool {
               
                CALayer *rootLayer = [[CALayer alloc] init];
                rootLayer.frame = CGRectMake(i*size.width, (maxY-j-1)*size.height, size.width, size.height);
                [self addBackgroundLayer:@[@0, @0, @(-0.1), @(M_PI), @0, @0, @0] rootLayer:rootLayer];
                
                CALayer *layer = [[CALayer alloc] init];
                layer.frame = CGRectMake(i*size.width, (maxY-j-1)*size.height, size.width, size.height);
                layer.frame = rootLayer.bounds;
                CGContextRef offscreenContext = CGBitmapContextCreate(NULL,
                                                                      size.width,
                                                                      size.height,
                                                                      8,
                                                                      0,
                                                                      CGImageGetColorSpace(screenshotImage.CGImage),kCGImageByteOrderDefault | kCGImageAlphaPremultipliedFirst);
                CGContextTranslateCTM(offscreenContext, -i*size.width, -j*size.height);
                CGContextDrawImage(offscreenContext, CGRectMake(0, 0, screenshotImage.size.width, screenshotImage.size.height), screenshotImage.CGImage);
                CGImageRef imageRef = CGBitmapContextCreateImage(offscreenContext);
                layer.contents = CFBridgingRelease(imageRef);
                CGContextRelease(offscreenContext);
                layer.backgroundColor = [UIColor blackColor].CGColor;
                //根据参数对CALayer进行偏移和旋转Transform
                CATransform3D transform = CATransform3DMakeTranslation(0, 0, 0.1);
                transform = CATransform3DRotate(transform, 0, 0, 0, 0);
                //设置transform属性并把Layer加入到主Layer中,这里的transform描述了layer所在的位置和形变等
                layer.transform = transform;
                [rootLayer addSublayer:layer];
                [animationView.layer addSublayer:rootLayer];
            }
        }
    }
    
    animationView.backgroundColor = [UIColor grayColor];
    
    for (CALayer *shape in animationView.layer.sublayers) {
        
        @autoreleasepool {
            
//            CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//            UIBezierPath *path = [UIBezierPath bezierPath];
//            [path moveToPoint:shape.position];
//            [path addLineToPoint:[self getEndPoint:shape]];
//            positionAnimation.path = path.CGPath;
            
            CGPoint endP = [self getEndPoint:shape];
            CAKeyframeAnimation *transformAnimation = [CAKeyframeAnimation animationWithKeyPath:@"sublayerTransform"];
            NSMutableArray *values = [NSMutableArray array];
            float angle = [self getRandRotateRangeValue:0.5 baseValue:0];
            int frames = MAX(shape.bounds.size.width, shape.bounds.size.height) / 2;
            NSArray *parm = [self getRotateAxis];
           
            NSArray *points = [self getTransformTranslateLayer:shape layerCoordinate:endP frame:frames];
            for (int i = 0; i < frames; i++) {
            NSArray *coordinateArr = points[i];
            CATransform3D transformRotate = CATransform3DMakeRotation(-angle, [[parm objectAtIndex:0] floatValue], [[parm objectAtIndex:1] floatValue], [[parm objectAtIndex:2] floatValue]);
            CATransform3D transformTranslate = CATransform3DMakeTranslation([[coordinateArr objectAtIndex:0] floatValue], [[coordinateArr objectAtIndex:1] floatValue], [[coordinateArr objectAtIndex:2] floatValue]);
            CATransform3D transform = CATransform3DConcat(transformRotate, transformTranslate);
            [values addObject:[NSValue valueWithCATransform3D:transform]];
            angle += [self getRandRotateRangeValue:0.06 baseValue:0];
            }
            transformAnimation.values = values;
            CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
            animationGroup.repeatCount = 1;
            animationGroup.animations = @[transformAnimation];
            animationGroup.duration = 1.0;
            animationGroup.delegate = self;
            [shape addAnimation:animationGroup forKey:@"handAnimation"];
        }
    }
}

- (NSArray *)getTransformTranslateLayer:(CALayer *)layer layerCoordinate:(CGPoint)layerPoint frame:(NSInteger)frame{
    
    NSMutableArray *totalPoints = [NSMutableArray array];
    CGPoint centerP = self.superview.center;
    CGFloat x1 = layer.frame.origin.x + layer.frame.size.width  *0.5;
    CGFloat y1 = layer.frame.origin.y + layer.frame.size.height *0.5;
    CGFloat x2 = centerP.x;
    CGFloat y2 = centerP.y;
    
    CGFloat x_distance = fabs(layerPoint.x - x1);
    CGFloat y_distance = fabs(layerPoint.y - y1);
    CGFloat x_var = fabs(x_distance)/frame; // x方向上的单位变量
    CGFloat y_var = fabs(y_distance)/frame; // y方向上的单位变量
    int i = 0;
    while (i < frame) {
        // 存放x,y,z轴上分别t要平移的坐标分量
        NSMutableArray *coordinateArr = [NSMutableArray array];
        
        CGFloat objx = 0;
        CGFloat objy = 0;
        CGFloat objz = 0;
        
        // 第一象限
        if (x1 <= x2 && y1 < y2) {
            
            objx =  -i*x_var;
            objy =  -i*y_var;
        }
        
        // 第二象限
        if (x1 > x2 && y1 <= y2) {
            
            objx = i*x_var;
            objy = -i*y_var;
        }
        
        // 第三象限
        if (x1 < x2 && y1 >= y2) {
            
            objx = -i*x_var;
            objy = i*y_var;
        }
        
        // 第四象限
        if (x1 > x2 && y1 >= y2) {
            
            objx = i*x_var;
            objy = i*y_var;
        }
        
        objz = sqrt(pow(objx, 2) + pow(objy, 2))*((float)rand()/(float)RAND_MAX * 3.0);
        [coordinateArr addObject:@(objx)];
        [coordinateArr addObject:@(objy)];
        [coordinateArr addObject:@(objz)];
        
        [totalPoints addObject:coordinateArr];
        i++;
    }

    return totalPoints;
}

- (CGFloat)getRandRotateRangeValue:(CGFloat)value baseValue:(CGFloat)baseValue{
    return  (float)rand()/(float)RAND_MAX * 2 * value + baseValue;
}

- (NSArray *)getRotateAxis{
    
    NSMutableArray *parmArr = [NSMutableArray array];
    NSArray *optionNumber = @[@(-1),@(0),@(1)];
    int i = 0;
    while (i<3) {
        
    int index = [self getRandOneRandThree];
    [parmArr addObject:optionNumber[index]];
        i++;
    }
    return parmArr;
    
}
- (int)getRandOneRandThree{
    return arc4random_uniform(3);
}
- (void)firstAddLayer:(NSArray*)params rootLayer:(CALayer *)rootLayer{
  
    CALayer *layer = [[CALayer alloc] init];
    layer.backgroundColor = [UIColor blueColor].CGColor;
    layer.bounds = CGRectMake(0, 0, square, square);
    layer.position = CGPointMake(CGRectGetMidX(rootLayer.bounds), CGRectGetMidY(rootLayer.bounds));
    //根据参数对CALayer进行偏移和旋转Transform
    CATransform3D transform = CATransform3DMakeTranslation([[params objectAtIndex:0] floatValue], [[params objectAtIndex:1] floatValue], [[params objectAtIndex:2] floatValue]);
    
    transform = CATransform3DRotate(transform, [[params objectAtIndex:3] floatValue], [[params objectAtIndex:4] floatValue], [[params objectAtIndex:5] floatValue], [[params objectAtIndex:6] floatValue]);
    //设置transform属性并把Layer加入到主Layer中,这里的transform描述了layer所在的位置和形变等
    layer.transform = transform;
    [rootLayer addSublayer:layer];
}

- (void)addBackgroundLayer:(NSArray *)params rootLayer:(CALayer *)rootLayer{
    
    CALayer *bgLayer = [[CALayer alloc] init];
    bgLayer.backgroundColor = [UIColor blackColor].CGColor;
    bgLayer.contentsScale = [UIScreen mainScreen].scale;
    bgLayer.bounds = CGRectMake(0, 0, square, square);
    bgLayer.position = CGPointMake(CGRectGetMidX(rootLayer.bounds), CGRectGetMidY(rootLayer.bounds));
    
    //根据参数对CALayer进行偏移和旋转Transform
    CATransform3D transform = CATransform3DMakeTranslation([[params objectAtIndex:0] floatValue], [[params objectAtIndex:1] floatValue], [[params objectAtIndex:2] floatValue]);
    
    transform = CATransform3DRotate(transform, [[params objectAtIndex:3] floatValue], [[params objectAtIndex:4] floatValue], [[params objectAtIndex:5] floatValue], [[params objectAtIndex:6] floatValue]);
    //设置transform属性并把Layer加入到主Layer中,这里的transform描述了layer所在的位置和形变等
    bgLayer.transform = transform;
    [rootLayer addSublayer:bgLayer];
}

- (CGFloat)productRandNumberWithRadio:(CGFloat)radio{
    
    return (float)rand()/(float)RAND_MAX*radio + radio;
}
// 计算layer所在直线与X轴的夹角
- (float)calculateAngle:(CALayer *)layer{
    
    CGPoint centerP = self.superview.center;
    CGFloat x1 = layer.frame.origin.x + layer.frame.size.width  *0.5;
    CGFloat y1 = layer.frame.origin.y + layer.frame.size.height *0.5;
    CGFloat x2 = centerP.x;
    CGFloat y2 = centerP.y;
    
    CGFloat y_var = fabs(y2-y1);
    CGFloat x_var = fabs(x2-x1);
    return atanf(y_var/x_var);
}

// layer运动路径
- (CGPoint)getEndPoint:(CALayer *)layer{
    
    CGPoint centerP = self.superview.center;
    CGFloat x1 = layer.frame.origin.x + layer.frame.size.width  *0.5;
    CGFloat y1 = layer.frame.origin.y + layer.frame.size.height *0.5;
    CGFloat x2 = centerP.x;
    CGFloat y2 = centerP.y;
    CGFloat r = self.superview.bounds.size.height*0.5 + 100;
   
   NSArray *points = [self getInsertPointBetweenCircleAndLineX1:x1 Y1:y1 X2:x2 Y2:y2 circleX:x2 circleY:y2 radius:r];
    // 第一象限
    if (x1 <= x2 && y1 < y2) {

        for (NSValue *value in points) {
            CGPoint valueP = [value CGPointValue];
            if (valueP.x < x2) {
                return valueP;
            }
        }
    }

    // 第二象限
    if (x1 > x2 && y1 <= y2) {
        
        for (NSValue *value in points) {
            CGPoint valueP = [value CGPointValue];
            if (valueP.x > x2) {
                return valueP;
            }
        }
    }

    // 第三象限
    if (x1 < x2 && y1 >= y2) {
        
        for (NSValue *value in points) {
            CGPoint valueP = [value CGPointValue];
            if (valueP.x < x2) {
                return valueP;
            }
        }
    }

    // 第四象限
    if (x1 > x2 && y1 >= y2) {
           
        for (NSValue *value in points) {
            CGPoint valueP = [value CGPointValue];
            if (valueP.x > x2) {
                return valueP;
            }
        }
    }
    
    return CGPointZero;
}
/**
  * 求圆和直线之间的交点
  * 直线方程:y = kx + b
  * 圆的方程:(x - m)² + (x - n)² = r²
  * x1, y1 = 线坐标1, x2, y2 = 线坐标2, m, n = 圆坐标, r = 半径
  */
- (NSArray *)getInsertPointBetweenCircleAndLineX1:(CGFloat)x1 Y1:(CGFloat) y1 X2:(CGFloat) x2 Y2:(CGFloat)y2 circleX:(CGFloat)m circleY:(CGFloat)n radius:(CGFloat)r {
    
    NSArray *kbArr = [self binaryEquationGetKBX1:x1 Y1:y1 X2:x2 Y2:y2];
    CGFloat k = [kbArr[0] floatValue];
    CGFloat b = [kbArr[1] floatValue];

    CGFloat aX = 1 + k * k;
    CGFloat bX = 2 * k * (b - n) - 2 * m;
    CGFloat cX = m * m + (b - n) * (b - n) - r * r;
    
    NSMutableArray *insertPoints = [NSMutableArray array];
    NSArray *xArr = [self quadEquationGetX:aX bX:bX cx:cX];
    
    for (NSNumber *obj in xArr) {
        CGFloat y = k *obj.floatValue + b;
        [insertPoints addObject:[NSValue valueWithCGPoint:CGPointMake(obj.floatValue, y)]];
    }
    
    return insertPoints;
}
/**
 * 求二元一次方程的系数
 * y1 = k * x1 + b => k = (y1 - b) / x1
 * y2 = k * x2 + b => y2 = ((y1 - b) / x1) * x2 + b
 */
- (NSArray *)binaryEquationGetKBX1:(CGFloat)x1  Y1:(CGFloat)y1 X2:(CGFloat)x2 Y2:(CGFloat)y2 {
    CGFloat k = (y1 - y2) / (x1 - x2);
    CGFloat b = (x1 * y2 - x2 * y1) / (x1 - x2);
    
    return @[@(k), @(b)];
}
/**
 * 一元二次方程求根
 * ax² + bx + c = 0
 */
- (NSArray *)quadEquationGetX:(CGFloat)a bX:(CGFloat)b cx:(CGFloat)c{
    
    NSMutableArray *xArr = [NSMutableArray array];
    CGFloat result = pow(b, 2) - 4 * a * c;
    if (result > 0) {
        CGFloat insertPointX1 = (-b + sqrt(result)) / (2 * a);
        CGFloat insertPointX2 = (-b - sqrt(result)) / (2 * a);
        [xArr addObject:@(insertPointX1)];
        [xArr addObject:@(insertPointX2)];
    } else if (result == 0) {
        CGFloat insertPointX = -b / (2 * a);
        [xArr addObject:@(insertPointX)];
    }
    return xArr;
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    UIView* animationView = (UIView*)objc_getAssociatedObject(self, @"boomAnimationView");
    if (animationView == nil) {
        return;
    }
    objc_removeAssociatedObjects(animationView);
    [animationView removeFromSuperview];
}

@end


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