最终效果

实现思路

动画的表现形式是颜色以及大小的变化,整体效果可以看做多个单独的波纹效果的叠加。因此我们可以创建多个CALayer,分别赋予CABasicAnimation动画,组成最终的动画效果。

因此我们先从单个波纹扩散效果来尝试,然后根据时间差将效果叠加起来。

代码

1.新建动画 View RippleAnimationView,动画效果在animationLayer上实现。

新建RippleAnimationView类,继承自UIView,设置扩散倍数,然后重写- (void)drawRect:(CGRect)rect方法,在方法内部新建承载动画的animationLayer。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#import
@interface RippleAnimationView : UIView
/**
 设置扩散倍数。默认1.423倍
 */
@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;
    }
     
    return self;
}
- (void)drawRect:(CGRect)rect {
     
    CALayer *animationLayer = [CALayer layer];
     
    // 加入动画
     
    [self.layer addSublayer:animationLayer];
}

2.创建单个扩散的动画承载CALayer,实现扩散效果。

首先实现缩放动画

1
2
3
4
5
6
7
8
9
10
- (CABasicAnimation *)scaleAnimation {
    CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
     
    scaleAnimation.fromValue = @1;
    scaleAnimation.toValue = @(_multiple);
    scaleAnimation.beginTime = CACurrentMediaTime();
    scaleAnimation.duration = 3;
    scaleAnimation.repeatCount = HUGE;// 重复次数设置为无限
    return scaleAnimation;
}

新建CALayer,并在layer上加载动画。然后将这个Layer放在animationLayer上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- (void)drawRect:(CGRect)rect {
     
    CALayer *animationLayer = [CALayer layer];
     
    // 新建缩放动画
    CABasicAnimation *animation = [self scaleAnimation];
     
    // 新建一个动画 Layer,将动画添加上去
    CALayer *pulsingLayer = [self pulsingLayer:rect animation:animation];
     
    //将动画 Layer 添加到 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(00, rect.size.width, rect.size.height);
    pulsingLayer.cornerRadius = rect.size.height / 2;
    [pulsingLayer addAnimation:animation forKey:@"plulsing"];
     
    return pulsingLayer;
}

可以看看现在的效果是这样的

3. 加入背景色以及边框色的渐变效果,将单一的缩放动画合并为动画组CAAnimationGroup。

(ps: 除了改变背景色,还要设置并改变边框色的更主要原因是去除锯齿)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// 设置一个初始化颜色的宏
#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];
     
    // 将三个动画合并为一个动画组
    CAAnimationGroup *animationGroup = [self animationGroupAnimations:animationArray];
     
    //修改方法,将原先添加的动画由“简单动画”改为“动画组”
    CALayer *pulsingLayer = [self pulsingLayer:rect animation:animationGroup];
     
    //将动画 Layer 添加到 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];
     
    return animationArray;
}
- (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);
    return scaleAnimation;
}
// 使用关键帧动画,使得颜色动画不要那么的线性变化
- (CAKeyframeAnimation *)backgroundColorAnimation {
    CAKeyframeAnimation *backgroundColorAnimation = [CAKeyframeAnimation animation];
     
    backgroundColorAnimation.keyPath = @"backgroundColor";
    backgroundColorAnimation.values = @[(__bridge id)ColorWithAlpha(255216870.5).CGColor,
                                        (__bridge id)ColorWithAlpha(2552311520.5).CGColor,
                                        (__bridge id)ColorWithAlpha(2552411970.5).CGColor,
                                        (__bridge id)ColorWithAlpha(2552411970).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(255216870.5).CGColor,
                                    (__bridge id)ColorWithAlpha(2552311520.5).CGColor,
                                    (__bridge id)ColorWithAlpha(2552411970.5).CGColor,
                                    (__bridge id)ColorWithAlpha(2552411970).CGColor];
    borderColorAnimation.keyTimes = @[@0.3,@0.6,@0.9,@1];
    return borderColorAnimation;
}
- (CALayer *)pulsingLayer:(CGRect)rect animation:(CAAnimationGroup *)animationGroup {
    CALayer *pulsingLayer = [CALayer layer];
     
    pulsingLayer.borderWidth = 0.5;
    pulsingLayer.borderColor = ColorWithAlpha(255216870.5).CGColor;
    pulsingLayer.frame = CGRectMake(00, rect.size.width, rect.size.height);
    pulsingLayer.cornerRadius = rect.size.height / 2;
    [pulsingLayer addAnimation:animationGroup forKey:@"plulsing"];
    return pulsingLayer;
}

现在就有种渐变的感觉了

4. 同时创建三个扩散动画的CALyer,将开始动画的时间错开,同时添加到animationLayer上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 设置静态常量 pulsingCount ,表示 Layer 的数量
static NSInteger const pulsingCount = 3;
// 设置静态常量 animationDuration ,表示动画时间
static double const animationDuration = 3;
- (void)drawRect:(CGRect)rect {
     
    CALayer *animationLayer = [CALayer layer];
     
    // 利用 for 循环创建三个动画 Layer
    for (int i = 0; i < pulsingCount; i++) {
        NSArray *animationArray = [self animationArray];
         
        // 通过传入参数 i 计算,错开动画时间
        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;
    return animationGroup;
}
... ...

然后效果有点……一言难尽……

真是很有纪律性的变化啊~~好吧,只需要加入动画曲线就好了

5. 最后加入动画速度曲线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
... ...
- (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;
     
    // 添加动画曲线。关于其他的动画曲线,也可以自行尝试
    animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
    return animationGroup;
}
... ...

如果需要点扩散,那就设置 frame 极小,同时扩散倍数增大即可。

将动画View垫在另一个圆形View之下即可实现最上方的效果。关闭背景色,重调边框色和边框宽度即可实现第二种效果。

iOS动画-扩散波纹效果的更多相关文章

  1. css3动画图片波纹效果

    这里的图片很有特点,下面有演示图片样式 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &quo ...

  2. ios 动画效果CATransition笔记

    初学ios开发,很多概念还不清楚,所以只有边学边做例子.又怕学了后面忘了前面,因此用自己的博客来纪录自己的学习历程,也是对自己学习不要懈怠做个监督. 刚学ios做动画效果.因为ios封装得很好,实现i ...

  3. iOS动画开发之五——炫酷的粒子效果

    在上几篇博客中,我们对UIView层的动画以及iOS的核心动画做了介绍,基本已经可以满足iOS应用项目中所有的动画需求,如果你觉得那些都还不够炫酷,亦或是你灵光一现,想用UIKit框架写出一款炫酷的休 ...

  4. ios点击产生波纹效果

    ios点击产生波纹效果 by 伍雪颖 - (void)viewDidLoad { [super viewDidLoad]; RippleView = [[UIView alloc] initWithF ...

  5. iOS开发——图形编程OC篇&粘性动画以及果冻效果

    粘性动画以及果冻效果 在最近做个一个自定义PageControl——KYAnimatedPageControl中,我实现了CALayer的形变动画以及CALayer的弹性动画,效果先过目: 先做个提纲 ...

  6. IOS 动画专题 --iOS核心动画

    iOS开发系列--让你的应用“动”起来 --iOS核心动画 概览 通过核心动画创建基础动画.关键帧动画.动画组.转场动画,如何通过UIView的装饰方法对这些动画操作进行简化等.在今天的文章里您可以看 ...

  7. iOS动画进阶 - 手摸手教你写ShineButton动画

    移动端访问不佳,请访问我的个人博客 前段时间在github上看见一个非常nice的动画效果,可惜是安卓的,想着用swift写一个iOS版的,下下来源代码研究了一下,下面是我写代码的心路历程 先上图和d ...

  8. Android自定义组件系列【14】——Android5.0按钮波纹效果实现

    今天任老师发表了一篇关于Android5.0中按钮按下的波纹效果实现<Android L中水波纹点击效果的实现>,出于好奇我下载了源代码看了一下效果,正好手边有一个Nexus手机,我结合实 ...

  9. jquery ripples水波纹效果( 涟漪效果)

    这个效果是我从bootstrap-material-design上面分离下来的,bootstrap-material-design的一些组件样式我不太不喜欢,但是非常喜欢这个水波纹效果,所以就有了这篇 ...

随机推荐

  1. httpd2.4.6三种工作模式(如何配置),防止占用内存暴增的策略

    之前偷懒默认用yum安装了httpd.后来发现服务器内存暴增,一度达到75% 打开一看,好嘛后台休眠进程全是httpd. 重启之后再度访问发现内存还是稳步增长. [root@iz2ze3ayxs2yp ...

  2. java定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积

    需求如下:(1)定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积. (2)定义一个类PassObject,在类中定义一个方法pri ...

  3. C盘扩展卷是灰色的扩容方法

    当想要扩容C盘的时候可能会发现C盘的扩展卷竟然是灰色的.原因是C盘旁边没有紧挨着的“”未分配空间“”, 只要将D盘的空间分出一些来就可以了. !!!磁盘的分区合并有风险,重要文件等记得先备份  !!! ...

  4. 八、Shell test 命令

    Shell test 命令 Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值.字符和文件三个方面的测试. 数值测试 参数 说明 -eq 等于则为真 -ne 不等于则为真 -gt ...

  5. 无屏幕和键盘配置树莓派WiFi和SSH

    原文转载:http://shumeipai.nxez.com/2017/09/13/raspberry-pi-network-configuration-before-boot.html 不算是什么新 ...

  6. Node.js 中流操作实践

    本文节选自 Node.js CheatSheet | Node.js 语法基础.框架使用与实践技巧,也可以阅读 JavaScript CheatSheet 或者 现代 Web 开发基础与工程实践 了解 ...

  7. A1025 PAT Ranking (25)(25 分)

    A1025 PAT Ranking (25)(25 分) Programming Ability Test (PAT) is organized by the College of Computer ...

  8. BFS:HDU2597-Dating with girls(2) (分时间标记状态)

    Dating with girls(2) Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  9. 华东交通大学2018年ACM“双基”程序设计竞赛 K

    MIKU酱是个玩游戏氪金的人,游戏公司给她制定了新的规则,如果想从关卡i到关卡j,你需要交一些钱就可以了,但同时,MIKU酱的爸爸zjw很爱她,所以她可以每过一关就向她爸要一次钱,但她爸每次给他的钱是 ...

  10. 文件的特殊权限(SUID,SGID,SBIT)

    文件的一般权限:r w x  对应 421  文件的特殊权限:SUID SGID SBIT对应 421  文件的隐藏权限:chattr设置隐藏权限,lsattr查看文件的隐藏权限. 文件访问控制列表: ...