码迷,mamicode.com
首页 > 其他好文 > 详细

动画特效七:碰撞动画

时间:2015-07-23 13:59:17      阅读:256      评论:0      收藏:0      [点我收藏+]

标签: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;

然后在AvaterView.m文件中,先进行相应的初始化工作。

- (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;
}

需要说明的一点是,代码 self.photoLayer.contents = (__bridge id) (image.CGImage)来完成图层图片的绘制工作。就是将图片image直接绘制到图层上面,而不是通过addsubView或者addsubLayer的形式添加到photoLayer上面。

然后我们对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";
}

至此,UI界面效果如下:

技术分享


对撞动画分析。

它们看起来是对撞动画,本质上就是让两个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);

对上面变量bounceXOffset和morphSize进行说明:

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];
        });
    }];

在执行动画之前,先用变量originalCenter保存原始中心点,接着使用弹簧动画,使头像运动到相撞点。停留1秒钟,接着又用一个类似的弹簧动画,循环执行上述动画,这样就完成了两者靠近并且循环执行的效果。效果图如下:

技术分享


二、碰撞为椭圆效果

- (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];

让两个头像的View执行CABasicAnimation动画的path效果即可。而toValue的值就是morphedFrame的椭圆效果,所以两者相撞时会产生视觉上面的椭圆效果。

版权声明:本文为博主原创文章,未经博主允许不得转载。

动画特效七:碰撞动画

标签:ui   catransform3d   core animation   core animation高级   animation   

原文地址:http://blog.csdn.net/sinat_27706697/article/details/47016877

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!