-
涉及到的技术点
- CoreAnimation的基本单位
- 图片弹跳
- 翻转动画
- 我们看到的现象是:如果以一个初速度往上抛一个物体,你的感觉是物体上抛过程花费的时间短,下落花费的时间长。所以我们在做动画中需要做这些违背物理规律的事情:即把下落的时间设置得比上弹的长。
-
JumpStar动画实现的思路
- 动画分为两个阶段:一个弹上去的阶段,一个落下来的阶段。弹上去的过程让视图绕 y 轴旋转 90 °,此时第一阶段的动画结束。在代理方法 animationDidStop 中开始第二个动画 —— 下落。在这个阶段一开始立刻替换图片, 随后在落下的同时让视图继续旋转 90°。我们要在下落动画结束之后 removeAllAnimations。
-
JumpStar动画的具体实现步骤
-
界面布局如图所示
ZQJumpStarView
的具体代码如下
// ZQJumpStarView.h #import
typedef enum : NSUInteger { NONMark, Mark, }STATE; @interface ZQJumpStarView : UIView /** 图片的状态 */ @property(nonatomic, assign, setter=setState:)STATE state; /** 被标记的图片 */ @property(nonatomic,strong)UIImage *markedImage; /** 未被标记的图片 */ @property(nonatomic, strong)UIImage *non_markedImage; /** 动画 */ -(void)animation; @end // ZQJumpStarView.m #import "ZQJumpStarView.h" /** 上升时间的宏 */ #define jumpDuration 0.125 /** 下降时间的宏 */ #define downDuration 0.215 @interface ZQJumpStarView() /** 图片View */ @property(nonatomic, strong)UIImageView *starView; /** 阴影View */ @property(nonatomic, strong)UIImageView *shadowView; @end @implementation ZQJumpStarView { /** 动画执行 */ BOOL animating; } -(instancetype)init { self = [super init]; if (self) { } return self; } /** 布局子视图 */ -(void)layoutSubviews { [super layoutSubviews]; self.backgroundColor = [UIColor clearColor]; if (self.starView == nil) { self.starView = [[UIImageView alloc] initWithFrame:CGRectMake(self.bounds.size.width / 2 - (self.bounds.size.width - 6) / 2, 0, self.bounds.size.width - 6, self.bounds.size.height - 6)]; self.starView.contentMode = UIViewContentModeScaleToFill; [self addSubview:self.starView]; } if (self.shadowView == nil) { self.shadowView = [[UIImageView alloc] initWithFrame:CGRectMake(self.bounds.size.width / 2 - 10 / 2, self.bounds.size.height - 3, 10, 3)]; self.shadowView.alpha = 0.4; self.shadowView.image = [UIImage imageNamed:@"shadow_new"]; [self addSubview:self.shadowView]; } } /** 图片状态的set方法 */ -(void)setState:(STATE)state { _state = state; self.starView.image = _state == Mark ? _markedImage : _non_markedImage; } /** 动画 */ -(void)animation { if (animating == YES) { return; } animating = YES; CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; transformAnimation.fromValue = @(0); transformAnimation.toValue = @(M_PI_2); transformAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"]; positionAnimation.fromValue = @(self.starView.center.y); positionAnimation.toValue = @(self.starView.center.y - 14); positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.duration = jumpDuration; animationGroup.fillMode = kCAFillModeForwards; animationGroup.removedOnCompletion = NO; animationGroup.delegate = self; animationGroup.animations = @[transformAnimation, positionAnimation]; [self.starView.layer addAnimation:animationGroup forKey:@"jumpUp"]; } /** 上升动画开始 */ -(void)animationDidStart:(CAAnimation *)anim { if ([anim isEqual:[self.starView.layer animationForKey:@"jumpUp"]]) { [UIView animateWithDuration:jumpDuration delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{ _shadowView.alpha = 0.2; _shadowView.bounds = CGRectMake(0, 0, _shadowView.bounds.size.width * 1.6, _shadowView.bounds.size.height); } completion:NULL]; } else if ([anim isEqual:[self.starView.layer animationForKey:@"jumpDown"]]) { [UIView animateWithDuration:jumpDuration delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{ _shadowView.alpha = 0.4; _shadowView.bounds = CGRectMake(0, 0, _shadowView.bounds.size.width / 1.6, _shadowView.bounds.size.height); } completion:NULL]; } } /** 下降动画开始 */ -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { if ([anim isEqual:[self.starView.layer animationForKey:@"jumpUp"]]) { self.state = self.state == Mark ? NONMark : Mark; NSLog(@"state:%ld", _state); CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; transformAnimation.fromValue = @(M_PI_2); transformAnimation.toValue = @(M_PI); transformAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"]; positionAnimation.fromValue = @(self.starView.center.y - 14); positionAnimation.toValue = @(self.starView.center.y); positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.duration = downDuration; animationGroup.fillMode = kCAFillModeForwards; animationGroup.removedOnCompletion = NO; animationGroup.delegate = self; animationGroup.animations = @[transformAnimation, positionAnimation]; [self.starView.layer addAnimation:animationGroup forKey:@"jumpDown"]; } else if ([anim isEqual:[self.starView.layer animationForKey:@"jumpDown"]]) { [self.starView.layer removeAllAnimations]; animating = NO; } } @end -
ViewController
的具体代码如下
// ViewController.m #import "ViewController.h" #import "ZQJumpStarView.h" @interface ViewController () /** 动画View */ @property (weak, nonatomic) IBOutlet ZQJumpStarView *jumpStarView; @end @implementation ViewController -(void)viewDidLoad { [super viewDidLoad]; [_jumpStarView layoutIfNeeded]; _jumpStarView.markedImage = [UIImage imageNamed:@"icon_star_incell"]; _jumpStarView.non_markedImage = [UIImage imageNamed:@"blue_dot"]; _jumpStarView.state = NONMark; } -(IBAction)tapped:(id)sender { [_jumpStarView animation]; } @end
-
-
运行结果如图所示