The final result

Implementation approach

The animation takes the form of color and size changes, and the overall effect can be seen as the superposition of multiple individual ripple effects. Therefore, we can create multiple Calayers and animate the CABasicAnimation to make up the final animation.

So we start with a single ripple diffusion effect and then stack it up according to the time difference.

code

1. Create a new animation ViewRippleAnimationView, the animation effect inanimationLayerOn the implementation.

Create a RippleAnimationView class that inherits from UIView, set the diffusion multiple, and override the – (void)drawRect:(CGRect)rect method to create the animationLayer inside the method that holds the animation.

#import <UIKit/UIKit.h>@interface RippleAnimationView: UIView /** Set diffusion multiplier. Default 1.423x */ @property (nonatomic, assign) CGFloat multiple; - (instancetype)initWithFrame:(CGRect)frame; @end @implementation RippleAnimationView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame];if (self) {
        self.backgroundColor = [UIColor clearColor];
       _multiple = 1.423;
    }
    
    returnself; } - (void)drawRect:(CGRect)rect { CALayer *animationLayer = [CALayer layer]; / / add animation [self layer addSublayer: animationLayer]; }Copy the code

2. Create a single diffuse animation bearerCALayerTo achieve the diffusion effect.

  1. First implement the zoom animation
- (CABasicAnimation *)scaleAnimation {
    CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; scaleAnimation.fromValue = @1; scaleAnimation.toValue = @(_multiple); scaleAnimation.beginTime = CACurrentMediaTime(); scaleAnimation.duration = 3; scaleAnimation.repeatCount = HUGE; // Set the number of repetitions to unlimitedreturn scaleAnimation;
}

Copy the code
  1. newCALayerAnd, inlayerLoad the animation on. And then take thisLayerOn theanimationLayerOn.
- (void)drawRect:(CGRect)rect { CALayer *animationLayer = [CALayer layer]; CABasicAnimation *animation = [self scaleAnimation]; CALayer *pulsingLayer = [self pulsingLayer:rect animation:animation]; // add animationLayer to animationLayer [animationLayer addSublayer:pulsingLayer]; [self.layer addSublayer:animationLayer]; } - (CALayer *)pulsingLayer:(CGRect)rect animation:(CABasicAnimation *)animation { CALayer *pulsingLayer = [CALayer layer]; PulsingLayer. BorderWidth = 0.5; pulsingLayer.borderColor = [UIColor blackColor].CGColor; pulsingLayer.frame = CGRectMake(0, 0, rect.size.width, rect.size.height); pulsingLayer.cornerRadius = rect.size.height / 2; [pulsingLayer addAnimation:animationforKey:@"plulsing"];
    
    return pulsingLayer;
}

Copy the code

And you can see what it looks like

3. Add background and border gradients to combine single zoom animations into animation groupsCAAnimationGroup.

(Ps: Besides changing the background color, the main reason to set and change the border color is to remove the sawtooth)

// Set a macro that initializes the color#define ColorWithAlpha(r,g,b,a) [UIColor colorWithRed: R /255.0 green: G /255.0 blue: B /255.0 alpha:a]- (void)drawRect:(CGRect)rect { CALayer *animationLayer = [CALayer layer]; NSArray *animationArray = [self animationArray]; / / merge three animations for an animation group CAAnimationGroup * animationGroup = [self animationGroupAnimations: animationArray]; CALayer *pulsingLayer = [self pulsingLayer:rect animation:animationGroup]; // add animationLayer to animationLayer [animationLayer addSublayer:pulsingLayer]; [self.layer addSublayer:animationLayer]; } - (NSArray *)animationArray { NSArray *animationArray = nil; CABasicAnimation *scaleAnimation = [self scaleAnimation]; CAKeyframeAnimation *borderColorAnimation = [self borderColorAnimation]; CAKeyframeAnimation *backgroundColorAnimation = [self backgroundColorAnimation]; animationArray = @[scaleAnimation, backgroundColorAnimation, borderColorAnimation];returnanimationArray; } - (CAAnimationGroup *)animationGroupAnimations:(NSArray *)array { CAAnimationGroup *animationGroup = [CAAnimationGroup  animation]; animationGroup.beginTime = CACurrentMediaTime(); animationGroup.duration = 3; animationGroup.repeatCount = HUGE; animationGroup.animations = array; animationGroup.removedOnCompletion = NO;return animationGroup;
}


- (CABasicAnimation *)scaleAnimation {
    CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    
    scaleAnimation.fromValue = @1;
    scaleAnimation.toValue = @(_multiple);
    returnscaleAnimation; } // Use keyframe animation, - (CAKeyframeAnimation *)backgroundColorAnimation {CAKeyframeAnimation *backgroundColorAnimation = [CAKeyframeAnimation animation]; backgroundColorAnimation.keyPath = @"backgroundColor"; BackgroundColorAnimation. Values = @ [(__bridge id) ColorWithAlpha (255, 216, 87, 0.5). The CGColor, (__bridge ID)ColorWithAlpha(255, 231, 152, 0.5).CGColor, (__bridge ID)ColorWithAlpha(255, 241, 197, 0.5).CGColor, (__bridge id)ColorWithAlpha(255, 241, 197, 0).CGColor]; BackgroundColorAnimation. KeyTimes = @ [@ 0.3, @ 0.6, @ 0.9, @ 1];return backgroundColorAnimation;
}

- (CAKeyframeAnimation *)borderColorAnimation {
    CAKeyframeAnimation *borderColorAnimation = [CAKeyframeAnimation animation];
    
    borderColorAnimation.keyPath = @"borderColor"; BorderColorAnimation. Values = @ [(__bridge id) ColorWithAlpha (255, 216, 87, 0.5). The CGColor, (__bridge ID)ColorWithAlpha(255, 231, 152, 0.5).CGColor, (__bridge ID)ColorWithAlpha(255, 241, 197, 0.5).CGColor, (__bridge id)ColorWithAlpha(255, 241, 197, 0).CGColor]; BorderColorAnimation. KeyTimes = @ [@ 0.3, @ 0.6, @ 0.9, @ 1];returnborderColorAnimation; } - (CALayer *)pulsingLayer:(CGRect)rect animation:(CAAnimationGroup *)animationGroup { CALayer *pulsingLayer = [CALayer  layer]; PulsingLayer. BorderWidth = 0.5; BorderColor = ColorWithAlpha(255, 216, 87, 0.5).CGColor; pulsingLayer.frame = CGRectMake(0, 0, rect.size.width, rect.size.height); pulsingLayer.cornerRadius = rect.size.height / 2; [pulsingLayer addAnimation:animationGroupforKey:@"plulsing"];

    return pulsingLayer;
}
Copy the code

Now it’s kind of a gradient

Create three diffusion animations at the same timeCALyer, stagger the start time of animation and add toanimationLayerOn.

Static NSInteger const pulsingCount = 3; Static double const animationDuration = 3; static double const animationDuration = 3; - (void)drawRect:(CGRect)rect { CALayer *animationLayer = [CALayer layer]; / / useforThe loop creates three animation layersfor(int i = 0; i < pulsingCount; i++) { NSArray *animationArray = [self animationArray]; / / by incoming parameters calculation, I stagger animation time CAAnimationGroup * animationGroup = [self animationGroupAnimations: animationArray index: I]; CALayer *pulsingLayer = [self pulsingLayer:rect animation:animationGroup]; [animationLayer addSublayer:pulsingLayer]; } [self.layer addSublayer:animationLayer]; }... . - (CAAnimationGroup *)animationGroupAnimations:(NSArray *)array index:(int)index { CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.beginTime = CACurrentMediaTime() + (double)(index * animationDuration) / (double)pulsingCount; animationGroup.duration = animationDuration; animationGroup.repeatCount = HUGE; animationGroup.animations = array; animationGroup.removedOnCompletion = NO;returnanimationGroup; }... .Copy the code

And then it kind of… It’s a long story…

This is a very disciplined change. Ok, just add the animation curve

5. Finally add the animation speed curve

. . - (CAAnimationGroup *)animationGroupAnimations:(NSArray *)array index:(int)index { CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.beginTime = CACurrentMediaTime() + (double)(index * animationDuration) / (double)pulsingCount; animationGroup.duration = animationDuration; animationGroup.repeatCount = HUGE; animationGroup.animations = array; animationGroup.removedOnCompletion = NO; // Add animation curves. About other animation curve, also can try animationGroup. TimingFunction = [CAMediaTimingFunctionfunctionWithName:kCAMediaTimingFunctionDefault];

    returnanimationGroup; }... .Copy the code

If point spread is required, set the frame to a minimum and increase the spread factor.

To achieve the topmost effect, place the animated View under another circular View. The second effect is achieved by turning off the background color and resetting the border color and width.

The last

The demo and the code are here.

Personal animation summaries are posted here in the animation practice series.

Welcome to exchange views [email protected].