利用动力系统做动画
很多朋友问我为什么喜欢做iOS动画,想要做出酷炫的动画,还不如直接去学艺术算了,其实我以前也有想过这个问题,现在我对这个问题的解释是.
1.美术上的动画是没有人机交互,事件点击的不同而产生不同的效果,就像漫画一样,也并不是说没有,只是美术设计师的重点不在于人机交互上面
2.美术设计的乐趣更多的是艺术上的审美,而iOS的动画更多的是算法上的美,和创意上的美.
3.最后最重要的一点就是,个人没有很好美术基础.
好了,不讲这么多了,今天现在来向大家介绍一个有意思的动画吧,首先先声明一下,这篇博文的动画并非原创!其原版是KITTEN YANG上的THE GUIDE TO IOSANIMATIOE 上的UIDynamaticAnimator教学上所用的DEMO,想学动画的同学也可以看看这本电子书,里头有很多非常棒的动画设计方法.
刚刚提到了UIDynamaticAnimator,那么什么是UIDynamaticAnimator呢?他是苹果至iOS7之后`为开发者提供的一套动力框架,主要用于一些动画,如重力动画,粘性动画,弹性动画,黑碰撞动画,粒子动画,今天我们就其中三个的三个行为进行分析,重力行为,碰撞行为,和粘性行为;
以下是成效图:
是否觉得非常酷炫吊炸天,说实话,如果我不知道这一套动力系统,做这一个动画可能华几个月都做不出啦,但是有了这一套动力系统框架再利用Core Graphic绘制绳子的贝塞尔曲线,着个动画将变得非常简单,一下是这个动画的渲染了控制试图的效果图
没错,看了渲染图,就一切都恍然大悟了,绳子只是一条简单的贝塞尔曲线,控制点就是两个灰色控制视图的中心点,下面是这个动画的.m文件下的封装;
//
// AnimaView.m
// ropeBallDemo
//
// Created by 叶杨 on 16/3/25.
// Copyright © 2016年 叶景天. All rights reserved.
//
#import "AnimaView.h"
@interface AnimaView ()
@property (nonatomic, strong)UIDynamicAnimator *animator;
//卡板的gravity重力行为
@property (nonatomic, strong)UIGravityBehavior *halfViewGravity;
//ballImageView和,两个控制视图的重力行为
@property (nonatomic, strong)UIGravityBehavior *controllViewGravity;
@property (nonatomic, strong)CAShapeLayer *shapeLayer;
//卡板视图view
@property (nonatomic, strong)UIView *halfView;
@property (nonatomic, strong)UIImageView *ballImageView;
@property (nonatomic, strong)UIView *upControllView;
@property (nonatomic, strong)UIView *downControllView;
@property (nonatomic, strong)UIView *middleView;
@end
@implementation AnimaView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self loadSubView];
}
return self;
}
- (void)loadSubView
{
//初始化卡板
self.halfView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height / 2)];
self.halfView.alpha = 0.5;
self.halfView.backgroundColor = [[UIColor alloc]initWithRed:0 green:0.5 blue:1 alpha:0.5];
[self addSubview:self.halfView];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]init];
[pan addTarget:self action:@selector(panAction:)];
[self.halfView addGestureRecognizer:pan];
//初始化排球图片
self.ballImageView = [[UIImageView alloc]initWithFrame:CGRectMake((self.bounds.size.width / 2) - 30, self.bounds.size.height / 1.5, 60, 60)];
self.ballImageView.image = [UIImage imageNamed:@"ball.png"];
[self addSubview:self.ballImageView];
//为图片添加阴影;
self.ballImageView.layer.cornerRadius = self.ballImageView.bounds.size.height / 2;
self.ballImageView.clipsToBounds = YES;
[self.ballImageView.layer setShadowOffset:CGSizeMake(-4, 4)];
[self.ballImageView.layer setShadowOpacity:0.5];
[self.ballImageView.layer setShadowRadius:5.0];
[self.ballImageView.layer setShadowColor:[UIColor grayColor].CGColor];
[self.ballImageView.layer setMasksToBounds:NO];
//初始化中间控制试图
self.middleView = [[UIView alloc]initWithFrame:CGRectMake(self.ballImageView.center.x - 15, 200, 30, 30)];
[self.middleView setBackgroundColor:[UIColor clearColor]];
[self addSubview:self.middleView];
//调整中间控制试图的位置,让它处于小球和卡板view的中间偏下的位置
[self.middleView setCenter:CGPointMake(self.middleView.center.x, (self.ballImageView.center.y-self.halfView.center.y)+15)];
//初始化顶部控制试图
self.upControllView = [[UIView alloc] initWithFrame:CGRectMake(self.ballImageView.center.x - 15, 200, 30, 30)];
[self.upControllView setBackgroundColor:[UIColor clearColor]];
[self addSubview:self.upControllView];
//调整self.upControllView的位置
[self.upControllView setCenter:CGPointMake(self.upControllView.center.x, (self.middleView.center.y - self.halfView.center.y) + self.halfView.center.y/2)];
//初始化下边的控制试图
self.downControllView = [[UIView alloc]initWithFrame:CGRectMake(self.ballImageView.center.x - 15, 200, 30, 30)];
[self.downControllView setBackgroundColor:[UIColor clearColor]];
[self addSubview:self.downControllView];
[self.downControllView setCenter:CGPointMake(self.downControllView.center.x, (self.middleView.center.y - self.halfView.center.y) + self.halfView.center.y*1.5)];
[self setUpBehavior];
}
- (void)setUpBehavior
{
self.animator = [[UIDynamicAnimator alloc]initWithReferenceView:self];
//为卡板添加重力行为
self.halfViewGravity = [[UIGravityBehavior alloc]initWithItems:@[self.halfView]];
[self.animator addBehavior:self.halfViewGravity];
//为上边和下边的控制试图添加重力行为
self.controllViewGravity = [[UIGravityBehavior alloc]initWithItems:@[self.ballImageView,self.upControllView,self.downControllView]];
__weak AnimaView *weakSelf = self;
//为controllViewGravity赋值,绳子的贝塞尔曲线,将在此处绘制,每当更显animator得时候,将会重新走这个Block方法
self.controllViewGravity.action = ^{
UIBezierPath *path=[[UIBezierPath alloc] init];
[path moveToPoint:weakSelf.halfView.center];
//绘制绳子的贝塞尔曲线,从卡板的center到小球的center,控制点为上下两个控制视图的view.center
[path addCurveToPoint:weakSelf.ballImageView.center controlPoint1:weakSelf.upControllView.center controlPoint2:weakSelf.downControllView.center];
if (!weakSelf.shapeLayer) {
weakSelf.shapeLayer = [[CAShapeLayer alloc] init];
weakSelf.shapeLayer.fillColor = [UIColor clearColor].CGColor;
weakSelf.shapeLayer.strokeColor = [UIColor colorWithRed:224.0/255.0 green:0.0/255.0 blue:35.0/255.0 alpha:1.0].CGColor;
weakSelf.shapeLayer.lineWidth = 5.0;
//Shadow
[weakSelf.shapeLayer setShadowOffset:CGSizeMake(-1, 2)];
[weakSelf.shapeLayer setShadowOpacity:0.5];
[weakSelf.shapeLayer setShadowRadius:5.0];
[weakSelf.shapeLayer setShadowColor:[UIColor blackColor].CGColor];
[weakSelf.shapeLayer setMasksToBounds:NO];
[weakSelf.layer insertSublayer:weakSelf.shapeLayer below:weakSelf.ballImageView.layer];
}
weakSelf.shapeLayer.path=path.CGPath;
};
[self.animator addBehavior:self.controllViewGravity];
//先为卡片添加撞击行为,以self.bounds的一般作为边界
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.halfView]];
[collision addBoundaryWithIdentifier:@"Left" fromPoint:CGPointMake(-1, 0) toPoint:CGPointMake(-1, [[UIScreen mainScreen] bounds].size.height)];
[collision addBoundaryWithIdentifier:@"Right" fromPoint:CGPointMake([[UIScreen mainScreen] bounds].size.width+1,0) toPoint:CGPointMake([[UIScreen mainScreen] bounds].size.width+1, [[UIScreen mainScreen] bounds].size.height)];
[collision addBoundaryWithIdentifier:@"Middle" fromPoint:CGPointMake(0, [[UIScreen mainScreen] bounds].size.height/2) toPoint:CGPointMake([[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height/2)];
[_animator addBehavior:collision];
//最后一步,也是最关键的一步,就是为卡片view上下两个控制试图,和最下面的一个ballImageView添加,粘性行为
//记住这里的哥哥attach设置锚点的偏移量要注意,只有锚点的偏移量设置得到好,才能够使绳子跳动起来
UIAttachmentBehavior *attach1 = [[UIAttachmentBehavior alloc]initWithItem:self.halfView offsetFromCenter:UIOffsetMake(1, 1) attachedToItem:self.upControllView offsetFromCenter:UIOffsetMake(0, 0)];
[_animator addBehavior:attach1];
UIAttachmentBehavior *attach2 = [[UIAttachmentBehavior alloc]initWithItem:self.upControllView offsetFromCenter:UIOffsetMake(0, 0) attachedToItem:self.downControllView offsetFromCenter:UIOffsetMake(0, 0)];
[_animator addBehavior:attach2];
UIAttachmentBehavior *attach3=[[UIAttachmentBehavior alloc] initWithItem:self.downControllView offsetFromCenter:UIOffsetMake(0, 0) attachedToItem:self.ballImageView offsetFromCenter:UIOffsetMake(0, -_ballImageView.bounds.size.height/2)];
[_animator addBehavior:attach3];
//最后还要给卡板和小球还有各个控制视图加一个框架.
UIDynamicItemBehavior *PanItem=[[UIDynamicItemBehavior alloc] initWithItems:@[self.halfView,self.upControllView,self.downControllView,self.ballImageView]];
PanItem.elasticity = 0.5;
[_animator addBehavior:PanItem];
}
- (void)panAction:(UIPanGestureRecognizer *)pan
{
CGPoint translation = [pan translationInView:pan.view];
//
if (!(pan.view.center.y + translation.y>(self.bounds.size.height/2)-(pan.view.bounds.size.height/2))) {
pan.view.center=CGPointMake(pan.view.center.x, pan.view.center.y+ translation.y);
[pan setTranslation:CGPointMake(0, 0) inView:pan.view];
}
if (pan.state==UIGestureRecognizerStateBegan) {
[self.animator removeBehavior:self.halfViewGravity];
// [_animator removeBehavior:_viewsGravity];
}
else if (pan.state==UIGestureRecognizerStateChanged){
}
else if (pan.state==UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled || pan
.state == UIGestureRecognizerStateFailed
) {
// self.ballImageView.center = CGPointMake(70, 100);
[self.animator addBehavior:self.halfViewGravity];
}
//通过这个方法可以让animator重新更新它所有的动力行为
[_animator updateItemUsingCurrentState:pan.view];
}
讲了这么多,动力系统剩下的类大家就可以自己研究了,代码花了一天才敲完(主要是具体参数的调试,非常烦人)具体Demo,大家可以在我的GITHUB上下载
下载地址是
https://github.com/bnb173yjx/DynamaticRopeBallDemo
@end