标签:ui catransform3d core animation core animation高级 animation
这一节讲述的动画效果是碰撞动画,就是模拟或者仿真现实物体的碰撞效果。先看看效果图。
动画效果分析:
1. 有两个形式一样的View(自己和对手),所以我们可以考虑直接封装一个View。
2. 注意到View的里面的图片及边框的圆形都有可能变成椭圆。所以使用View的block方式实现这个效果有点不可靠。我们可以考虑使用图层动画。
View的层次结构图如下:
1. 因为每个View有自己的name,所以封装的View是继承自UIView而不是UIImageView。
2. photoLayer是为了显示图片。
3. maskLayer是为了实现圆角效果。
4. circleLayer就是图片外面的圆圈。
有了上面的分析,我们自定义一个头像的View (AvatarView) 并且它应该有图片及名称两个属性。
@property (nonatomic, strong) UIImage *image; @property (nonatomic, copy) NSString *name;
- (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { [self initSubs]; } return self; } - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self initSubs]; } return self; } - (void)initSubs { self.photoLayer = [CALayer layer]; self.circleLayer = [CAShapeLayer layer]; self.maskLayer = [CAShapeLayer layer]; self.label = [[UILabel alloc] init]; self.label.font = [UIFont fontWithName:@"ArialRoundedMTBold" size:18.0]; self.label.textAlignment = NSTextAlignmentCenter; self.label.textColor = [UIColor blackColor]; [self.layer addSublayer:self.photoLayer]; self.photoLayer.mask = self.maskLayer; [self.layer addSublayer:self.circleLayer]; [self addSubview:self.label]; } - (void)setImage:(UIImage *)image { _image = image; self.photoLayer.contents = (__bridge id)(image.CGImage); } - (void)setName:(NSString *)name { _name = [name copy]; self.label.text = name; }
然后我们对AvatarView中的元素进行布局工作,代码如下:
- (void)layoutSubviews { [super layoutSubviews]; //Size the avatar image to fit CGFloat width = self.bounds.size.width; CGFloat height = self.bounds.size.height; CGFloat photoLayerX = (width - self.image.size.width + borderWidth) * 0.5; CGFloat photoLayerY = (height - self.image.size.height - borderWidth) * 0.5; self.photoLayer.frame = CGRectMake(photoLayerX, photoLayerY, self.image.size.width, self.image.size.height); //Draw the circle self.circleLayer.path = [UIBezierPath bezierPathWithOvalInRect:self.bounds].CGPath; self.circleLayer.strokeColor = [UIColor whiteColor].CGColor; self.circleLayer.lineWidth = borderWidth; self.circleLayer.fillColor = [UIColor clearColor].CGColor; //Size the layer self.maskLayer.path = self.circleLayer.path; self.maskLayer.position = CGPointMake(0, 10); //Size the label self.label.frame = CGRectMake(0, height + 10, width, 24); }
方法bezierPathWithOvalInRect 就是在指定的矩形区域获取它的内切圆或者内切椭圆。而这里是正方形,所以得到的头像显示出来的就是圆形。
然后在ViewController中的viewDidload方法中调用自定义的AvatarView并完成其初始化工作,代码如下:
- (void)viewDidLoad { [super viewDidLoad]; self.opponentAvatar.image = [UIImage imageNamed:@"empty"]; self.myAvatar.image = [UIImage imageNamed:@"avatar-1"]; self.myAvatar.name = @"Me"; }
对撞动画分析。
它们看起来是对撞动画,本质上就是让两个View往对方靠近,当达到一定的位置(即两者相遇)时,改变photoLayer和circleLayer的呈现形态(椭圆型)。
我们以屏幕正中心为基准线,最终两者相撞时,它们的中心点的X值分别为:屏幕中心点x - 头像宽度 / 2,屏幕中心点x + 头像宽度 / 2。所以两者最终相撞的代码如下:
CGSize avatarSize = self.myAvatar.frame.size; // 理论上bounceXOffset的值是avatarSize.width / 2, 但考虑到图片有边框,所以适当进行调节 CGFloat bounceXOffset = avatarSize.width / 1.9; CGSize morphSize = CGSizeMake(avatarSize.width * 0.85, avatarSize.height * 1.1); CGPoint rightBouncePoint = CGPointMake(self.view.frame.size.width * 0.5 + bounceXOffset, self.myAvatar.center.y); CGPoint leftBouncePoint = CGPointMake(self.view.frame.size.width * 0.5 - bounceXOffset, self.myAvatar.center.y);
1. 偏离的距离应该是头像宽度 / 2, 但这里是除以1.9, 目的是为了相撞时候,椭圆的显示效果更加明显。
2. morphSize 就是两者相撞时候的形态,明显看出将原来头像的宽度压缩,高度拉伸。这样,到时候取内切图形的时候,就是大家看到的椭圆效果。
在AvatarView中定义一个方法,主要负责动画对撞的效果。
- (void)bounceOffPoint:(CGPoint)bouncePoint morphSie:(CGSize)morphSize;
我们在上面定义的方法中,写入以下代码。
CGPoint originalCenter = self.center; //Damping值越小,弹性越大 [UIView animateWithDuration:animationDuration delay:0.0 usingSpringWithDamping:0.8 initialSpringVelocity:0.0 options:UIViewAnimationOptionCurveLinear animations:^{ self.center = bouncePoint; } completion:^(BOOL finished) { }]; [UIView animateWithDuration:animationDuration delay:animationDuration usingSpringWithDamping:0.7 initialSpringVelocity:0.1 options:UIViewAnimationOptionCurveLinear animations:^{ self.center = originalCenter; } completion:^(BOOL finished) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ <span style="white-space:pre"> </span>[self bounceOffPoint:bouncePoint morphSie:morphSize]; }); }];
二、碰撞为椭圆效果
- (void)bounceOffPoint:(CGPoint)bouncePoint morphSie:(CGSize)morphSize;在上述方法的底部继续添加以下代码
CGRect rightMorphFrame = CGRectMake(0, self.bounds.size.height - morphSize.height, morphSize.width, morphSize.height); CGRect leftMorphFrame = CGRectMake(self.bounds.size.width - morphSize.width, self.bounds.size.height - morphSize.height, morphSize.width, morphSize.height); CGRect morphedFrame = (originalCenter.x > bouncePoint.x) ? rightMorphFrame : leftMorphFrame; CABasicAnimation *morphAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; morphAnimation.duration = animationDuration; morphAnimation.toValue = (__bridge id)([UIBezierPath bezierPathWithOvalInRect:morphedFrame].CGPath); morphAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; [self.circleLayer addAnimation:morphAnimation forKey:nil]; [self.maskLayer addAnimation:morphAnimation forKey:nil];
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:ui catransform3d core animation core animation高级 animation
原文地址:http://blog.csdn.net/sinat_27706697/article/details/47016877