软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 移动开发 -> 动画特效七:碰撞动画 -> 正文阅读

[移动开发]动画特效七:碰撞动画


这一节讲述的动画效果是碰撞动画,就是模拟或者仿真现实物体的碰撞效果。先看看效果图。
[img]http://img.blog.csdn.net/20150723092155431?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
动画效果分析:
1. 有两个形式一样的View(自己和对手),所以我们可以考虑直接封装一个View。
2. 注意到View的里面的图片及边框的圆形都有可能变成椭圆。所以使用View的block方式实现这个效果有点不可靠。我们可以考虑使用图层动画。
View的层次结构图如下:
[img]http://img.blog.csdn.net/20150723093756982?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
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界面效果如下:
[img]http://img.blog.csdn.net/20150723100911581?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
对撞动画分析。
它们看起来是对撞动画,本质上就是让两个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秒钟,接着又用一个类似的弹簧动画,循环执行上述动画,这样就完成了两者靠近并且循环执行的效果。效果图如下:
[img]http://img.blog.csdn.net/20150723121229800?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
二、碰撞为椭圆效果
- (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的椭圆效果,所以两者相撞时会产生视觉上面的椭圆效果。
版权声明:本文为博主原创文章,未经博主允许不得转载。
......显示全文...
    点击查看全文


上一篇文章      下一篇文章      查看所有文章
2015-07-23 15:26:59  
移动开发 最新文章
深入了解android中的消息机制Handler
Android
Libgdx之BitmapFont字体
AndroidApp发布到应用市场的流程
Android开发找工作之前先看看这些知识点吧
View的事件分发机制解析
简单介绍了解白鹭引擎Egret
Cocos2d
android获取本地图片(二)
动画特效七:碰撞动画
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2017年11日历
2017-11-19 16:30:58
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --