软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 移动开发 -> 动画特效七:碰撞动画 -> 正文阅读
移动开发 最新文章
深入了解android中的消息机制Handler
Android
Libgdx之BitmapFont字体
AndroidApp发布到应用市场的流程
Android开发找工作之前先看看这些知识点吧
View的事件分发机制解析
简单介绍了解白鹭引擎Egret
Cocos2d
android获取本地图片(二)
动画特效七:碰撞动画

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

  2015-07-23 15:35:29

这一节讲述的动画效果是碰撞动画,就是模拟或者仿真现实物体的碰撞效果。先看看效果图。

动画效果分析:
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的椭圆效果,所以两者相撞时会产生视觉上面的椭圆效果。
版权声明:本文为博主原创文章,未经博主允许不得转载。
上一篇文章      下一篇文章      查看所有文章
2015-07-23 15:26:59  
360图书馆 论文大全 母婴/育儿 软件开发资料 网页快照 文字转语音 购物精选 软件 美食菜谱 新闻中心 电影下载 小游戏 Chinese Culture
生肖星座解梦 三沣玩客 拍拍 视频 开发 Android开发 站长 古典小说 网文精选 搜图网 天下美图 中国文化英文 多播视频 装修知识库
2017-1-18 20:03:13
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --