【iOS】點贊動畫(UIView動畫 幀動畫 粒子動畫)

前言

今天來寫一下項目裏用到的一個點贊效果吧,最主要的也就是這個粒子動畫了,打算本身寫一個,由於參考了幾個文章沒有發現封裝的比較現成的代碼,最終的效果圖看一下:
點贊動畫.gif

這個gif展現出來的效果跟真機或者模擬器顯示的仍是有差別的,在模擬器和真機上運行動畫很流暢(LICEcap最近出了問題因此沒用,湊合使用了QQ上的錄屏功能)git

思路

  • 選用控件:

有點擊事件,有圖片有數字,有狀態,UIButton最好github

  • 動畫拆分:

1.粒子動畫的粒子生成,以後擴散,到達必定位置後消失 (粒子動畫) 2.上邊+1數字,漸變出現,達到必定y值時隱藏 (UIView動畫或幀動畫) 3.右邊的點贊數字,由0隱藏,透明度漸變出現,變爲紅色 (UIView動畫或幀動畫) 4.按鈕由灰變紅就不用說了,設置按鈕圖片就好bash

  • 注意點:

連續點擊可能出現的網絡請求問題( 點贊/取消點贊操做網絡延遲問題 )服務器

具體實現

一 . 粒子動畫

//1.建立一個能放射粒子的cell(粒子源)
    CAEmitterCell * explosionCell = [CAEmitterCell emitterCell];
    //透明度變化速度
    explosionCell.alphaSpeed = -1.0;
    //透明度變化範圍
    explosionCell.alphaRange = 0.5;
    //粒子存在時間
    explosionCell.lifetime = 0.5;
    //粒子存在時間的範圍
    explosionCell.lifetimeRange = 0.2;
    //每一個cell能夠釋放多少個粒子
    explosionCell.birthRate = 15;
    //粒子擴散的速度
    explosionCell.velocity = 75.f;
    //粒子擴散的速度上下區間 +10 or -10
    //    explosionCell.velocityRange = 10.f;
    //最大 - M_PI_4/2  粒子發射方向
    explosionCell.emissionLongitude = - M_PI_2;
    explosionCell.emissionRange = M_PI_2;
    //粒子形變大小
    explosionCell.scale = 0.08;
    //形變範圍
    //    explosionCell.scaleRange = 0.02;
    //粒子內容
    explosionCell.contents = (id)[[UIImage imageNamed:@"spark_red"] CGImage];
    //粒子初始顏色
//    explosionCell.color = [UIColor yellowColor].CGColor;
    explosionCell.color = [UIColor redColor].CGColor;
    //粒子其餘混合顏色
    explosionCell.redRange = 10;
    explosionCell.greenRange = 10;
    explosionCell.blueRange = 10;
    //混合色顏色變化速度
    explosionCell.redSpeed = 10;
    explosionCell.greenSpeed = 10;
    explosionCell.blueSpeed = 10;
    
    
    // 2.發射源
    CAEmitterLayer * explosionLayer = [CAEmitterLayer layer];
    [self.layer addSublayer:explosionLayer];
    self.explosionLayer = explosionLayer;
    //發射位置 - 粒子從哪裏出現開始擴散
    //self.explosionLayer.emitterSize = CGSizeMake(self.bounds.size.width + 3, self.bounds.size.height + 3);
    //發射源的形狀
    explosionLayer.emitterShape = kCAEmitterLayerPoint;
    //每秒發射cell的數量
    explosionLayer.birthRate = 0;
    //發射模式: 從發射體的哪一個位置發出粒子: 某個點,表面,邊緣,內部
    explosionLayer.emitterMode = kCAEmitterLayerVolume;
    //粒子的渲染模式
    explosionLayer.renderMode = kCAEmitterLayerAdditive;
    
    explosionLayer.emitterCells = @[explosionCell];

複製代碼

1.粒子動畫的核心就是這些代碼了,建立釋放粒子的粒子源cell,由發射源發射這些粒子源cell,這些cell擴散出粒子,造成粒子動畫網絡

2.在建立粒子源cell後設置的屬性,有的是關於粒子源自己,有的是關於擴散出來的粒子,其中帶有Range的屬性,這個是上下浮動的範圍值,即區間,例如粒子的生命週期lifetime = 0.5,設置lifetimeRange = 0.2,那麼粒子真正的生命週期是0.3 ~ 0.7,即粒子真正的生命週期是0.3 ~ 0.7的隨機值,隨機值的屬性多了,就造成了無規則的動畫ide

  1. emissionLongitude = - M_PI_2 表明垂直向上的方向

二 . 拳頭動畫

拳頭的放大效果用幀動畫實現,animation.keyPath = @"transform.scale"動畫

/**
 *  拳頭放大動畫
 */
- (void)enlargementAnimation {
    
    //經過鍵幀動畫實現縮放
    CAKeyframeAnimation * animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"transform.scale";
    animation.values = @[@1.5,@2.0,@3.5];
    animation.duration = 0.25;
    animation.calculationMode = kCAAnimationCubic;
    self.backImageView.alpha = 0.05;
    [self.backImageView.layer addAnimation:animation forKey:nil];
    
}
複製代碼

三.兩個數字動畫

- (void)countAnimation {
    
    //1.添加數字+1透明度動畫
    CAKeyframeAnimation *animation0 = [CAKeyframeAnimation animation];
    animation0.keyPath = @"opacity";
    animation0.values = @[@0.5,@0.8,@1.0];
    animation0.duration = 0.5;
    animation0.calculationMode = kCAAnimationCubic;
    [self.incLabel.layer addAnimation:animation0 forKey:nil];
    //開始動畫時"+1"上升label回到起始位置
    self.incLabel.y = _incOrginY;
    //防止label閃爍
    self.incLabel.alpha = 1;
    
    //2.添加"+1"慢慢變大動畫
    CAKeyframeAnimation *animationScale = [CAKeyframeAnimation animation];
    animationScale.keyPath = @"transform.scale";
    animationScale.values = @[@1.0,@1.1,@1.2];
    animationScale.duration = 1.0;
    animationScale.calculationMode = kCAAnimationCubic;
    [self.incLabel.layer addAnimation:animationScale forKey:nil];
    
    //3.添加"+1"s向上位移動畫
    __weak typeof(self) weakSelf = self;
    [UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        
        weakSelf.incLabel.y = self->_incOrginY - 18;
        
    } completion:^(BOOL finished) {
        //4.添加"+1"慢慢消失動畫
        CAKeyframeAnimation *animation2 = [CAKeyframeAnimation animation];
        animation2.keyPath = @"opacity";
        animation2.values = @[@0.8,@0.5,@0];
        animation2.duration = 0.5;
        animation2.calculationMode = kCAAnimationCubic;
        [weakSelf.incLabel.layer addAnimation:animation2 forKey:nil];
        self.incLabel.alpha = 0;
    }];
}
複製代碼

使用

  • demo地址在文章末尾
  • 使用相對比較簡單一些,導入頭文件,建立button,設置button的Action便可
#import "RBCLikeButton.h"
//1.建立點贊按鈕
RBCLikeButton *likeBtn = [[RBCLikeButton alloc] initWithFrame:CGRectMake(130, 200, 100, 100)];
[likeBtn setImage:[UIImage imageNamed:@"day_like"] forState:UIControlStateNormal];
[likeBtn setImage:[UIImage imageNamed:@"day_like_red"] forState:UIControlStateSelected];
[likeBtn addTarget:self action:@selector(likeBtnClickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:likeBtn];

複製代碼
  • 點贊對應的Action,代碼稍微長了一點點,不過是附帶網絡請求和網絡請求不一樣情況做出的處理操做,具體的解釋代碼裏面所有包含了
//點贊/取消點贊操做
- (void)likeBtnClickAction:(RBCLikeButton *)btn {
    
    //1.記錄是不是點贊操做
    BOOL isThump = !btn.isSelected;
    
    //2.點贊量,正式運用中可自定義(從服務器獲取當前的點贊量)
    NSInteger num = btn.thumpNum;
    
    //3.計算點贊後數值
    if (isThump) {
        num = num + 1;//點贊後的點贊數
    }else{
        num = num - 1;//取消點贊後的點贊數
    }
    
    //4.調用點贊動畫,設置點贊button的點贊數值
    [btn setThumbWithSelected:isThump thumbNum:num animation:YES];
    
    //5.網絡請求
    if (isThump) {//若是是點贊操做
        
        //發起網絡請求,告知服務器APP進行了點贊操做,服務器的返回結果爲isRequestSuccess
        
        BOOL isRequestSuccess = YES;//請求成功
        
        if (!isRequestSuccess) {//若是操做失敗(沒有網絡或接口異常)
            
            //取消剛纔的點贊操做致使的數值變換和點贊按鈕的狀態變化
            [btn cancelLike];
        }
    }else{//若是是取消點贊操做
        
        //發起網絡請求,告知服務器APP進行了取消點贊操做,服務器的返回結果爲isRequestSuccess
        
        BOOL isRequestSuccess = NO;//請求失敗
        
        if (!isRequestSuccess) {//若是操做失敗(沒有網絡或接口異常)
            
            //恢復到點贊以前的點贊數值和點贊按鈕的狀態變化
            [btn recoverLike];
        }
    }
}
複製代碼

爲何先改變本地按鈕的狀態,而後再網絡請求呢?ui

  • 之因此這麼作是爲了用戶體驗考慮,直觀的展示給他操做的結果,不讓他發覺感受不到的網絡請求(由於可能存在網絡延遲的狀況)
  • 咱們也準備了網絡請求失敗的補救方法recoverLike和cancelLike,這樣就能夠作好防備了

注意點

其實點贊Action的代碼仍是不夠完善 spa

還有問題麼.png

由於在實操過程當中會出現用戶反覆狂擊點贊按鈕的操做,而網絡請求又是有延遲的,這樣就可能出現兩次點贊請求同時發送給了服務器,儘管有些服務器會作限制,可是你本地的代碼邏輯將會混亂,形成點贊數顯示錯誤的問題code

解決辦法:定義點贊按鈕的狀態

typedef enum : NSUInteger {
    RBCLikeButtonStatusHadThumbs,        //已點贊
    RBCLikeButtonStatusNoneThumbs,       //未點贊
    RBCLikeButtonStatusThumbsing,        //正在點贊
    RBCLikeButtonStatusCancelThumbsing   //正在取消點贊
} RBCLikeButtonStatus;
複製代碼

定義status爲點贊按鈕的點贊狀態

//若是status不是"正在取消點贊""正在點贊""已點贊"的狀態時,再執行點贊網絡請求
if (   status != RBCLikeButtonStatusCancelThumbsing 
    && status != RBCLikeButtonStatusThumbsing 
    && status != RBCLikeButtonStatusHadThumbs   ) 
{
      //改變本地點贊按鈕model的點贊狀態 --> 正在點贊
      status = RBCLikeButtonStatusThumbsing;      
      //執行點讚的網絡請求                
}
複製代碼
//若是status不是"正在取消點贊""正在點贊""未點贊"的狀態時,再執行取消點贊網絡請求
if (   status != RBCLikeButtonStatusCancelThumbsing 
    && status != RBCLikeButtonStatusThumbsing 
    && status != RBCLikeButtonStatusNoneThumbs   ) 
{
      //改變本地點贊按鈕model的點贊狀態 -->正在取消點贊
      status = RBCLikeButtonStatusCancelThumbsing;      
      //執行取消點讚的網絡請求                
}
複製代碼

總結

1.有朋友能有更好的思路的話能夠與做者交流,有誤的地方歡迎指正

2.Demo地址: github.com/TynnPassBy/…

有問題請評論區留言.jpg
相關文章
相關標籤/搜索