仿潮汐和顶空的气泡变化效果

首先是效果展示

这里是有一个气泡的样式,在做无规则的边界、大小、位移变化。


23916860-3ed465a753cd7344.gif

这里的实现,我考虑使用CAShapeLayer和UIBezierPath结合实现,这里动画用的是CABasicAnimation的path属性动画。
先贴出所有代码,后续有时间,逐步分析。

这是.h文件

#import <UIKit/UIKit.h>
#import "BubbleModel.h"

NS_ASSUME_NONNULL_BEGIN

@interface BubbleView : UIView

/**  */
typedef void(^__nullable ScaleAnimStartCompletion)(BubbleModel *model);

@property (nonatomic,copy) NSArray <BubbleModel*>*scaleArr;
@property (nonatomic,copy) ScaleAnimStartCompletion ScaleAnimStart;

-(void)startScaleAni;
-(void)endScaleAni;

@end

NS_ASSUME_NONNULL_END

这是.m文件


#import "BubbleView.h"

@interface BubbleView ()<CAAnimationDelegate>

@property (nonatomic,assign) CGFloat width;

@property (nonatomic,assign) CGFloat height;

@property (nonatomic,strong) CAShapeLayer *myShapeLayer;

@property (nonatomic,strong) UIBezierPath *myTempToValue;

@property (nonatomic,assign) NSInteger scaleArrIndex;

@end

@implementation BubbleView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setUI];
    }
    return self;
}

-(void)setUI {
    //
    self.backgroundColor = [UIColor clearColor];
    //
    UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
    effectView.alpha = 0.8;
    [self addSubview:effectView];
    [effectView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(0);
    }];
    //
    self.width = self.frame.size.width;
    self.height = self.frame.size.height;
    //
    self.myShapeLayer = [[CAShapeLayer alloc]init];
    self.myTempToValue = [[UIBezierPath alloc]init];
    //
    self.myShapeLayer.frame = self.bounds;
    self.myShapeLayer.path = [self myToValue].CGPath;
    //
    self.layer.mask = self.myShapeLayer;
//    [self.layer addSublayer:self.myShapeLayer];
    //
    [self startAnimate];
}

-(void)startAnimate {
    [self.myShapeLayer addAnimation:[self pathBasicAnimate] forKey:@"pathBasicAnimate"];
}

-(CABasicAnimation*)pathBasicAnimate {
    CABasicAnimation *anima = [CABasicAnimation animationWithKeyPath:@"path"];
    anima.delegate = self;
    anima.removedOnCompletion = false;
    anima.repeatCount = 1;
    anima.duration = 2;
    anima.fillMode = kCAFillModeForwards;
    //
    UIBezierPath *path = [self myToValue];
    anima.toValue = (__bridge id _Nullable)(path.CGPath);
    //
    self.myTempToValue = path;
    //
    return anima;
}

- (UIBezierPath *)myToValue {
    //
    CGFloat R = 110;
    //
    CGFloat aR = [self getRandomCGFloatWithFrom:R-10 WithTo:R+10];
    CGFloat bR = [self getRandomCGFloatWithFrom:R-10 WithTo:R+10];
    CGFloat cR = [self getRandomCGFloatWithFrom:R-10 WithTo:R+10];
    CGFloat dR = [self getRandomCGFloatWithFrom:R-10 WithTo:R+10];
    CGFloat centerX = self.width/2;
    CGFloat centerY = self.height/2;
    CGFloat minF = 0.4;
    CGFloat maxF = 0.75;
    CGFloat offP = 5;
    //
    UIBezierPath *path = [[UIBezierPath alloc]init];
    ///AB段
    //
    CGPoint pointA = CGPointMake(centerX-aR, centerY);
    CGPoint pointB = CGPointMake(centerX, centerY-bR);
    //
    CGPoint controlAB1 = CGPointMake(pointA.x + [self getRandomCGFloatWithFrom:-offP WithTo:offP], pointA.y - bR * [self getRandomCGFloatWithFrom:minF WithTo:maxF]);
    CGPoint controlAB2 = CGPointMake(pointB.x - aR * [self getRandomCGFloatWithFrom:minF WithTo:maxF], pointB.y + [self getRandomCGFloatWithFrom:-offP WithTo:offP]);
    [path moveToPoint:pointA];
    [path addCurveToPoint:pointB controlPoint1:controlAB1 controlPoint2:controlAB2];
    ///BC段
    //
    CGPoint pointC = CGPointMake(pointB.x+cR, pointA.y);
    //
    CGPoint controlBC1 = CGPointMake(pointB.x + cR * [self getRandomCGFloatWithFrom:minF WithTo:maxF], pointB.y + [self getRandomCGFloatWithFrom:-offP WithTo:offP]);
    CGPoint controlBC2 = CGPointMake(pointC.x + [self getRandomCGFloatWithFrom:-offP WithTo:offP], pointC.y - bR * [self getRandomCGFloatWithFrom:minF WithTo:maxF]);
    [path addCurveToPoint:pointC controlPoint1:controlBC1 controlPoint2:controlBC2];
    ///CD段
    //
    CGPoint pointD = CGPointMake(pointB.x, pointC.y + dR);
    //
    CGPoint controlCD1 = CGPointMake(pointC.x + [self getRandomCGFloatWithFrom:-offP WithTo:offP], pointC.y + dR*[self getRandomCGFloatWithFrom:minF WithTo:maxF]);
    CGPoint controlCD2 = CGPointMake(pointD.x + cR * [self getRandomCGFloatWithFrom:minF WithTo:maxF], pointD.y + [self getRandomCGFloatWithFrom:-offP WithTo:offP]);
    [path addCurveToPoint:pointD controlPoint1:controlCD1 controlPoint2:controlCD2];
    ///DA段
    //
    CGPoint controlDA1 = CGPointMake(pointD.x - aR*[self getRandomCGFloatWithFrom:minF WithTo:maxF], pointD.y + [self getRandomCGFloatWithFrom:-offP WithTo:offP]);
    CGPoint controlDA2 = CGPointMake(pointA.x + [self getRandomCGFloatWithFrom:-offP WithTo:offP], pointA.y + dR*[self getRandomCGFloatWithFrom:minF WithTo:maxF]);
    [path addCurveToPoint:pointA controlPoint1:controlDA1 controlPoint2:controlDA2];
    //
    return path;
}

///获取随机小数
- (CGFloat)getRandomCGFloatWithFrom:(CGFloat)from WithTo:(CGFloat)to {
    //
    NSInteger tempInt1 = (NSInteger)(from*100);
    NSInteger tempInt2 = (NSInteger)(to*100);
    
    NSInteger Rnum = [self getRandomNumberWithFrom:tempInt1 WithTo:tempInt2];
    
    CGFloat temp = (CGFloat)Rnum/100;
    
    return temp;
}


- (NSInteger)getRandomNumberWithFrom:(NSInteger)from WithTo:(NSInteger)to {
    NSInteger temp = arc4random() % (to - from + 1);
    return (from + temp);
}

//
- (void)animationDidStart:(CAAnimation *)anim {
//    NSLog(@"%s-%d-%@",__FUNCTION__,__LINE__,[NSThread currentThread]);
}

//
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    //1
    if (anim == [self.myShapeLayer animationForKey:@"pathBasicAnimate"]) {
        //
        self.myShapeLayer.path = self.myTempToValue.CGPath;
        //
        [self startAnimate];
    }
    
    if (anim == [self.myShapeLayer animationForKey:@"scaleBasicAnimate"]) {
        //
        [self startScale];
    }
}

//-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//
//    CABasicAnimation *anima = [self.myShapeLayer animationForKey:@"scaleBasicAnimate"];
//    //
//    if (anima != nil) {
//        //
//        self.scaleArr = @[
//            [[BubbleModel alloc]initWithFromValue:1 WithToValue:1 WithDuration:1]
//        ];
//        self.scaleArrIndex = 0;
//        [self startScale];
//    }
//    else{
//        //
//        self.scaleArr = @[
//            [[BubbleModel alloc]initWithFromValue:1 WithToValue:0.5 WithDuration:3],
//            [[BubbleModel alloc]initWithFromValue:0.5 WithToValue:1.5 WithDuration:7],
//            [[BubbleModel alloc]initWithFromValue:1.5 WithToValue:1.5 WithDuration:3],
//            [[BubbleModel alloc]initWithFromValue:1.5 WithToValue:1 WithDuration:3]
//        ];
//        self.scaleArrIndex = 0;
//        [self startScale];
//    }
//}


-(void)startScaleAni {
    //
    self.scaleArrIndex = 0;
    [self startScale];
}
-(void)endScaleAni {
    //
    //    self.scaleArrIndex = 0;
    //    [self startScale];
//    self.scaleArr = @[];
    [self.myShapeLayer removeAnimationForKey:@"scaleBasicAnimate"];
}

-(void)startScale {
    //
    if (self.scaleArrIndex >= 0 && self.scaleArrIndex < self.scaleArr.count) {
        //
        BubbleModel *model = self.scaleArr[self.scaleArrIndex];
        //
        if (model.isShake) {
            //震动一下
            [self impactFeedbackGenerator];
        }
        //
        [self.myShapeLayer addAnimation:[self scaleBasicAnimateWithFromValue:model.fromValue WithToValue:model.toValue WithDuration:model.duration] forKey:@"scaleBasicAnimate"];
        //
        self.scaleArrIndex += 1;
        //
        self.ScaleAnimStart(model);
    }
    else{
        self.scaleArrIndex = 1;
        [self startScale];
    }
}

-(CABasicAnimation*)scaleBasicAnimateWithFromValue:(CGFloat)fromValue WithToValue:(CGFloat)toValue WithDuration:(CFTimeInterval)duration {
    //
    CABasicAnimation *animate = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    animate.delegate = self;
    animate.removedOnCompletion = false;
    animate.fillMode = kCAFillModeForwards;
    animate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    //
    animate.duration = duration;
    //
    animate.fromValue = [NSNumber numberWithFloat:fromValue];
    animate.toValue =  [NSNumber numberWithFloat:toValue];
    //
    return animate;
}

- (void)impactFeedbackGenerator {
    //
    UIImpactFeedbackGenerator *obj = [[UIImpactFeedbackGenerator alloc]initWithStyle:UIImpactFeedbackStyleHeavy];
    [obj prepare];
    [obj impactOccurred];
}


@end
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容