这是一篇基于CAReplicatorLayer动画的文章,原文《The power of CAReplicatorLayer》是用swift实现的,看到原作者的动画,真的很棒,学习后用Objective-C又实现了一遍,在这里分享给大家。
为了方便调用,我把相关方法写成了类方法。调用每个方法会返回一个CAReplicatorLayer的实例,然后把该实例添加到目标试图的layer上即可。
首先是公开的外部接口,具体如下:
#import <Foundation/Foundation.h>
@interface EXAnimation : NSObject
+ (CALayer *)replicatorLayer_Circle;
+ (CALayer *)replicatorLayer_Wave;
+ (CALayer *)replicatorLayer_Triangle;
+ (CALayer *)replicatorLayer_Grid;
@end
接下来是内部实现,因为会用到一些CAAnimationGroup,子动画我都分开写了,所以篇幅不少,具体如下:
#import "EXAnimation"
@implementation EXAnimation
+ (CALayer *)replicatorLayer_Circle{
CAShapeLayer *shape = [CAShapeLayer layer];
shape.frame = CGRectMake(0, 0, 80, 80);
shape.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 80, 80)].CGPath;
shape.fillColor = [UIColor redColor].CGColor;
shape.opacity = 0.0;
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = @[[EXAnimation alphaAnimation],[EXAnimation scaleAnimation]];
animationGroup.duration = 4.0;
animationGroup.autoreverses = NO;
animationGroup.repeatCount = HUGE;
[shape addAnimation:animationGroup forKey:@"animationGroup"];
CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
replicatorLayer.frame = CGRectMake(0, 0, 80, 80);
replicatorLayer.instanceDelay = 0.5;
replicatorLayer.instanceCount = 8;
[replicatorLayer addSublayer:shape];
return replicatorLayer;
}
+ (CALayer *)replicatorLayer_Wave{
CGFloat between = 5.0;
CGFloat radius = (100-2*between)/3;
CAShapeLayer *shape = [CAShapeLayer layer];
shape.frame = CGRectMake(0, (100-radius)/2, radius, radius);
shape.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, radius, radius)].CGPath;
shape.fillColor = [UIColor redColor].CGColor;
[shape addAnimation:[EXAnimation scaleAnimation1] forKey:@"scaleAnimation"];
CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
replicatorLayer.frame = CGRectMake(0, 0, 100, 100);
replicatorLayer.instanceDelay = 0.2;
replicatorLayer.instanceCount = 3;
replicatorLayer.instanceTransform = CATransform3DMakeTranslation(between*2+radius,0,0);
[replicatorLayer addSublayer:shape];
return replicatorLayer;
}
+ (CALayer *)replicatorLayer_Triangle{
CGFloat radius = 100/4;
CGFloat transX = 100 - radius;
CAShapeLayer *shape = [CAShapeLayer layer];
shape.frame = CGRectMake(0, 0, radius, radius);
shape.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, radius, radius)].CGPath;
shape.strokeColor = [UIColor redColor].CGColor;
shape.fillColor = [UIColor redColor].CGColor;
shape.lineWidth = 1;
[shape addAnimation:[EXAnimation rotationAnimation:transX] forKey:@"rotateAnimation"];
CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
replicatorLayer.frame = CGRectMake(0, 0, radius, radius);
replicatorLayer.instanceDelay = 0.0;
replicatorLayer.instanceCount = 3;
CATransform3D trans3D = CATransform3DIdentity;
trans3D = CATransform3DTranslate(trans3D, transX, 0, 0);
trans3D = CATransform3DRotate(trans3D, 120.0*M_PI/180.0, 0.0, 0.0, 1.0);
replicatorLayer.instanceTransform = trans3D;
[replicatorLayer addSublayer:shape];
return replicatorLayer;
}
+ (CALayer *)replicatorLayer_Grid{
NSInteger column = 3;
CGFloat between = 5.0;
CGFloat radius = (100 - between * (column - 1))/column;
CAShapeLayer *shape = [CAShapeLayer layer];
shape.frame = CGRectMake(0, 0, radius, radius);
shape.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, radius, radius)].CGPath;
shape.fillColor = [UIColor redColor].CGColor;
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = @[[EXAnimation scaleAnimation1], [WHAnimation alphaAnimation]];
animationGroup.duration = 1.0;
animationGroup.autoreverses = YES;
animationGroup.repeatCount = HUGE;
[shape addAnimation:animationGroup forKey:@"groupAnimation"];
CAReplicatorLayer *replicatorLayerX = [CAReplicatorLayer layer];
replicatorLayerX.frame = CGRectMake(0, 0, 100, 100);
replicatorLayerX.instanceDelay = 0.3;
replicatorLayerX.instanceCount = column;
replicatorLayerX.instanceTransform = CATransform3DTranslate(CATransform3DIdentity, radius+between, 0, 0);
[replicatorLayerX addSublayer:shape];
CAReplicatorLayer *replicatorLayerY = [CAReplicatorLayer layer];
replicatorLayerY.frame = CGRectMake(0, 0, 100, 100);
replicatorLayerY.instanceDelay = 0.3;
replicatorLayerY.instanceCount = column;
replicatorLayerY.instanceTransform = CATransform3DTranslate(CATransform3DIdentity, 0, radius+between, 0);
[replicatorLayerY addSublayer:replicatorLayerX];
return replicatorLayerY;
}
+ (CABasicAnimation *)alphaAnimation{
CABasicAnimation *alpha = [CABasicAnimation animationWithKeyPath:@"opacity"];
alpha.fromValue = @(1.0);
alpha.toValue = @(0.0);
return alpha;
}
+ (CABasicAnimation *)scaleAnimation{
CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform"];
scale.fromValue = [NSValue valueWithCATransform3D:CATransform3DScale(CATransform3DIdentity, 0.0, 0.0, 0.0)];
scale.toValue = [NSValue valueWithCATransform3D:CATransform3DScale(CATransform3DIdentity, 1.0, 1.0, 0.0)];
return scale;
}
+ (CABasicAnimation *)scaleAnimation1{
CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform"];
scale.fromValue = [NSValue valueWithCATransform3D:CATransform3DScale(CATransform3DIdentity, 1.0, 1.0, 0.0)];
scale.toValue = [NSValue valueWithCATransform3D:CATransform3DScale(CATransform3DIdentity, 0.2, 0.2, 0.0)];
scale.autoreverses = YES;
scale.repeatCount = HUGE;
scale.duration = 0.6;
return scale;
}
+ (CABasicAnimation *)rotationAnimation:(CGFloat)transX{
CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform"];
CATransform3D fromValue = CATransform3DRotate(CATransform3DIdentity, 0.0, 0.0, 0.0, 0.0);
scale.fromValue = [NSValue valueWithCATransform3D:fromValue];
CATransform3D toValue = CATransform3DTranslate(CATransform3DIdentity, transX, 0.0, 0.0);
toValue = CATransform3DRotate(toValue,120.0*M_PI/180.0, 0.0, 0.0, 1.0);
scale.toValue = [NSValue valueWithCATransform3D:toValue];
scale.autoreverses = NO;
scale.repeatCount = HUGE;
scale.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
scale.duration = 0.8;
return scale;
}
@end
接下来是调用各个接口的动画效果,我做了一个截图(如下),如果要深入学习,可以去原文去学习,又较为详细的讲解。
CGFloat w = UIScreen.mainScreen.bounds.size.width;
CGFloat radius = (w-5*3)/2;
NSMutableArray *sublayers = [NSMutableArray arrayWithCapacity:4];
[sublayers addObject:[EXAnimation replicatorLayer_Circle:radius]];
[sublayers addObject:[EXAnimation replicatorLayer_Wave:radius]];
[sublayers addObject:[EXAnimation replicatorLayer_Triangle:radius]];
[sublayers addObject:[EXAnimation replicatorLayer_Grid:radius]];
[sublayers addObject:[EXAnimation replicatorLayer_Trans:radius]];
for(NSInteger loop = 0; loop < sublayers.count; loop++){
NSInteger col = loop%2;
NSInteger row = loop/2;
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(5+(radius+5)*col, 64+5+(radius+5)*row, radius, radius)];
v.backgroundColor = [UIColor colorWithRed:248/255.0 green:248/255.0 blue:248/255.0 alpha:1];
[v.layer addSublayer:sublayers[loop]];
[self.contentView addSubview:v];
}