If you like me, Thumb Up!

 

端午节本来想出去玩耍,但是可恶的天气。好吧,在屋子里接着写代码。之后一个月就要安心复习了。

这次做的东西受到了Facebook Paper的like按钮影响,也自己做了一个来玩。其实网上早就有现成的轮子,但是个人觉得他们的封装太繁琐,所以自己按照自己的实现思路来实现了一下。

拆解动画

这次写的动画要简单的多。看到上面的成果图之后,很显然的就能看出分成两部分。

  • 按钮缩放动画
  • 粒子爆炸动画

下面我们逐一攻克。

缩放动画

除去粒子爆炸效果,就是一个大拇指的缩放动画。为了让体验最佳,当我们手指按下按钮的时候,最好有一个动画的过渡来告知按钮已被按下。而在所以动画动作,最好的体现手法就是按钮的放缩,这也符合物理中力学的按压动作,使物体产生形变。

至于实现起来就很容易了,我们用transform属性,对目标View添加放缩动画,再按帧加载,来控制View的变换情况。帧动画在之前的Particle Explosion Effect有所提及,即使不看讲解直接看代码的参数名称也是一目了然的,这里直接给出代码,不再讲解:

#pragma mark - Methods
- (void) clickButtonPress {
    if (isSelected) [self popInsideWithDuration: 0.5];
    else {
        [self popOutsideWithDuration: 0.5];
        // [self.explodeAnimationView animate];
    };
}

- (void) popOutsideWithDuration: (NSTimeInterval) duringTime {
    __weak typeof(self) weakSelf = self;
    self.transform = CGAffineTransformIdentity;
    
    [UIView animateKeyframesWithDuration: duringTime delay: 0 options: 0 animations: ^{
        [weakSelf setImage: [UIImage imageNamed: @"Like-Blue"] forState: UIControlStateNormal];
        [UIView addKeyframeWithRelativeStartTime: 0
                                relativeDuration: 1 / 3.0
                                      animations: ^{
                                          typeof(self) strongSelf = weakSelf;
                                          strongSelf.transform = CGAffineTransformMakeScale(1.5, 1.5);
                                      }];
        [UIView addKeyframeWithRelativeStartTime: 1 / 3.0
                                relativeDuration: 1 / 3.0
                                      animations: ^{
                                          typeof(self) strongSelf = weakSelf;
                                          strongSelf.transform = CGAffineTransformMakeScale(0.8, 0.8);
                                      }];
        [UIView addKeyframeWithRelativeStartTime: 2 / 3.0
                                relativeDuration: 1 / 3.0
                                      animations: ^{
                                          typeof(self) strongSelf = weakSelf;
                                          strongSelf.transform = CGAffineTransformMakeScale(1.0, 1.0);
                                      }];
    } completion: ^(BOOL finished) {
        isSelected = YES;
    }];
}

- (void) popInsideWithDuration: (NSTimeInterval) duringTime {
    __weak typeof(self) weakSelf = self;
    self.transform = CGAffineTransformIdentity;
    
    [UIView animateKeyframesWithDuration: duringTime delay: 0 options: 0 animations: ^{
        [weakSelf setImage: [UIImage imageNamed: @"Like-PlaceHold"] forState: UIControlStateNormal];
        [UIView addKeyframeWithRelativeStartTime: 0
                                relativeDuration: 1 / 2.0
                                      animations: ^{
                                          typeof(self) strongSelf = weakSelf;
                                          strongSelf.transform = CGAffineTransformMakeScale(0.8, 0.8);
                                      }];
        [UIView addKeyframeWithRelativeStartTime: 1 / 2.0
                                relativeDuration: 1 / 2.0
                                      animations: ^{
                                          typeof(self) strongSelf = weakSelf;
                                          strongSelf.transform = CGAffineTransformMakeScale(1.0, 1.0);
                                      }];
    } completion: ^(BOOL finished) {
        isSelected = NO;
    }];

}

粒子爆炸动画

这里的爆炸,我们不需要像之前的气泡爆炸一样,来切割View。想法是之前消息气泡的动画是为了让消息气泡有一种破裂的感觉,所以我们直接对视图进行粒子分割。而这里的粒子动画,就好像是拇指视图在经过按压之后,在墙面上敲击震动后扬起的灰尘,这些粒子不是属于原视图的。我们在多次观看最终结果图后,发现这是最合理的思路。

当我们确定思路后,我们会发现工作量减少了很多。因为我们使用CALayer的一个高性能原生粒子引擎来解决CAEmitterLayer。详细的相关属性和方法可以查看CAEmitterLayer官方文档。这里还是简单的介绍一下这个类,我们可以把CAEmitterLayer当做许多CAEmitterCell的容器,每一个cell也就是我们细化出的每一刻粒子。然而我们只需要定义一个CAEmitterLayer粒子效果来作为粒子样式模板即可,CAEmitterLayer负责基于模板实例化该样式的粒子流,使他们同时刻以动画形式表现。

以下是初始化CAEmitterLayer以及CAEmitterCell的代码:

#pragma mark - Initial Function
- (void) StartUp {
    self.clipsToBounds = NO;
    self.userInteractionEnabled = NO;
    
    CAEmitterCell *emitter = [CAEmitterCell emitterCell];
    
    emitter.contents                = (id)[UIImage imageNamed: @"Like-Sparkle"].CGImage;
    emitter.name                    = @"explosion";
    emitter.alphaRange              = 0.2f;
    emitter.alphaSpeed              = -1.f;
    emitter.lifetime                = 0.7f;
    emitter.lifetimeRange           = 0.3f;
    emitter.birthRate               = 0;
    emitter.velocity                = 40.0f;
    emitter.velocityRange           = 10.0f;
    emitter.emissionRange           = M_PI_4;
    emitter.scale                   = 0.05f;
    emitter.scaleRange              = 0.02;
    
    _emitterLayer = [CAEmitterLayer layer];
    _emitterLayer.name              = @"emitterLayer";
    _emitterLayer.emitterShape      = kCAEmitterLayerCircle;
    _emitterLayer.emitterMode       = kCAEmitterLayerOutline;
    _emitterLayer.emitterPosition   = self.center;
    _emitterLayer.emitterSize       = CGSizeMake(25, 0);
    _emitterLayer.renderMode        = kCAEmitterLayerOldestFirst;
    _emitterLayer.masksToBounds     = NO;
    _emitterLayer.emitterCells      = @[emitter];
    _emitterLayer.frame             = [UIScreen mainScreen].bounds;
    
    [self.layer addSublayer: _emitterLayer];
    
    _emitterLayer.emitterPosition   = self.center;
}

CAMitterCell 的属性基本上可以分为 3 种:

  • 这种粒子的某一属性的初始值。比如,color属性指定了一个可以混合图片内容颜色的混合色。
  • 粒子某一属性的变化范围。比如 emissionRange 属性的值是2π,这意味着例子可以从360度任意位置反射出来。如果指定一个小一些的值,就可以创造出一个圆锥形。
  • 指定值在时间线上的变化。

总结了以上一般性规律,我们对 CAMitterCell 的认识又进了一步。我想,你以及可以用他来制作火焰或者下雪的动效了。对吧~

写在最后

以上就是简单的点赞按钮动画效果。

在制作完这个样例 push 到 github 上之后,我还配置了 CocoaPods 安装方式(这也是我第一个支持 CocoaPods 的开源小库,走个国际范嘛~)。有机会的话,下次我们来分享如何装载 .podspec 文件,最近要开始突击期末考试了,暂停写代码,说不定下次再分享作品就是在上海了~


DGThumbUpButton Github