iOS 動畫 —— LOVE

看了CBStoreHouseRefreshControl 效果,一直想模仿一下,先來感受下原GIF圖:

CBStoreHouseRefreshControl

然后想著今天七夕,于是也模仿這這個想寫一個 Love 的組成,但是單身的我貌似不好寫 LOVE, 就讓 LOVE 少一個 E 吧!

loving_you

具體實現基本參照CBStoreHouseRefreshControl,只是將那個刷新方面的先抽離出來啦,這一塊具體的基本思路:

  • 畫單個線,并組成我們要的形狀
  • 讓線變成閃亮的,并讓線持續的閃亮
  • 最后讓所有的線飛起來

直接先看代碼實現吧:


#import "ShowView.h"
#import "ShowLineItem.h"

static const CGFloat kdisappearDuration = 1.2; // 消失的時間
static const CGFloat kloadingTimingOffset = 0.1; //
static const CGFloat kloadingIndividualAnimationTiming = 0.8; // 每一個Item 閃亮的時間
static const CGFloat kbarDarkAlpha = 0.4; // 透明度,變向改變顏色

@interface ShowView ()

@property (nonatomic, strong) NSArray *lineItems; // 所有的線
@property (nonatomic, strong) CADisplayLink *displayLink; // 計時器
@property (nonatomic, assign) CGFloat disappearProgress; // 消失的時間
@property (nonatomic) BOOL reverseLoadingAnimation; // 是否一次轉圈完成

@end

@implementation ShowView

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        
        // 通過位置確定我們要畫的
        NSArray *startPoints = @[@"{1,10}",@"{1,90}",@"{60,40}",@"{60,90}",@"{100,90}",@"{100,40}",@"{120,40}",@"{140,90}"];
        NSArray *endPoints = @[@"{1,90}",@"{40,90}",@"{60,90}",@"{100,90}",@"{100,40}",@"{60,40}",@"{140,90}",@"{160,40}"];
        // 確定Frame
        CGFloat width = 0;
        CGFloat height = 0;
        for (int i = 0; i < startPoints.count; i++) {
            
            CGPoint startPoint = CGPointFromString(startPoints[i]);
            CGPoint endPoint = CGPointFromString(endPoints[i]);
            
            if (startPoint.x > width) width = startPoint.x;
            if (endPoint.x > width) width = endPoint.x;
            if (startPoint.y > height) height = startPoint.y;
            if (endPoint.y > height) height = endPoint.y;
        }
        // 將我們這個View的Frame 重新擴大一下 == 2 是為了讓線條不受邊界影響
        self.frame = CGRectMake(0, 0, width + 2, height + 2);


        NSMutableArray *mutableBarItems = [[NSMutableArray alloc] init];
        for (int i=0; i<startPoints.count; i++) {
            
            CGPoint startPoint = CGPointFromString(startPoints[i]);
            CGPoint endPoint = CGPointFromString(endPoints[i]);
            
            ShowLineItem *lineItem = [[ShowLineItem alloc] initWithFrame:self.frame startPoint:startPoint endPoint:endPoint color:[UIColor whiteColor] lineWidth:2.0];
            lineItem.tag = i;
            lineItem.backgroundColor=[UIColor clearColor];
            lineItem.alpha = 0;
            [mutableBarItems addObject:lineItem];
            [self addSubview:lineItem];
            
            [lineItem setHorizontalRandomness:self.frame.size.width + 20 dropHeight:self.frame.size.height + 20];
        }
        
        self.lineItems = [NSArray arrayWithArray:mutableBarItems];
        self.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, 0);
     
        for (ShowLineItem *lineItem in self.lineItems) {
            [lineItem setupWithFrame:self.frame];
        }
        self.transform = CGAffineTransformMakeScale(1.0, 1.0);
        // 一進入的默認形式
        self.reverseLoadingAnimation = NO; // 閃亮的方向
        // 默認已近完成好搭建
        [self updateBarItemsWithProgress:1.0];
        // 假設一開始就動畫,后期去掉
        [self startAnimation];
        // 假設 在20 秒后 停止,后期去掉
        [self performSelector:@selector(stopAnimation) withObject:nil afterDelay:20];
    }
    return self;
}

// 實際上設置
- (void)updateBarItemsWithProgress:(CGFloat)progress {

    for (ShowLineItem *lineItem in self.lineItems) {
        NSInteger index = [self.lineItems indexOfObject:lineItem];
        CGFloat startPadding = (1 - 0.5) / self.lineItems.count * index;
        CGFloat endPadding = 1 - 0.5 - startPadding;
        
        if (progress == 1 || progress >= 1 - endPadding) {
            lineItem.transform = CGAffineTransformIdentity;
            lineItem.alpha = kbarDarkAlpha;
        }
        else if (progress == 0) {
          [lineItem setHorizontalRandomness:self.frame.size.width  dropHeight:self.frame.size.height * 2];
        }
        else {
            CGFloat realProgress ;
            if (progress <= startPadding)
                realProgress = 0;
            else
                realProgress = MIN(1, (progress - startPadding)/0.5);
            lineItem.transform = CGAffineTransformMakeTranslation(lineItem.translationX*(1-realProgress), -(self.frame.size.height * 2)*(1-realProgress));
            lineItem.transform = CGAffineTransformRotate(lineItem.transform, M_PI*(realProgress));
            lineItem.transform = CGAffineTransformScale(lineItem.transform, realProgress, realProgress);
            lineItem.alpha = realProgress * kbarDarkAlpha;
        }
    }
}

#pragma mark 開始之后的動畫操作
- (void)startAnimation {

    // 順序或反序
    if (self.reverseLoadingAnimation) {
        for (NSInteger i= self.lineItems.count - 1; i >= 0; i--) {
            ShowLineItem *lineItem = [self.lineItems objectAtIndex:i];
            [self performSelector:@selector(barItemAnimation:) withObject:lineItem afterDelay:(self.lineItems.count -i -1)*kloadingTimingOffset inModes:@[NSRunLoopCommonModes]];
        }
    }
    else {
        for (NSInteger i = 0; i < self.lineItems.count; i++) {
            ShowLineItem *lineItem = [self.lineItems objectAtIndex:i];
            [self performSelector:@selector(barItemAnimation:) withObject:lineItem afterDelay:i*kloadingTimingOffset inModes:@[NSRunLoopCommonModes]];
        }
    }

}

- (void)barItemAnimation:(ShowLineItem*)lineItem
{
    lineItem.alpha = 1.0f;
    [lineItem.layer removeAllAnimations];
    [UIView animateWithDuration:kloadingIndividualAnimationTiming animations:^{
        lineItem.alpha = kbarDarkAlpha;
    } completion:^(BOOL finished) {
        
    }];
        
    BOOL isLastOne;
    if (self.reverseLoadingAnimation) {
        isLastOne = lineItem.tag == 0;
    }else {
        isLastOne = lineItem.tag == self.lineItems.count - 1;
    }
    
    if (isLastOne ) {
        [self startAnimation];
    }
}

#pragma mark 結束之后的動畫操作
- (void)stopAnimation {
    
    for (ShowLineItem *lineItem in self.lineItems) {
        [lineItem.layer removeAllAnimations];
        lineItem.alpha = kbarDarkAlpha;
    }
    
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateDisappearAnimation)];
    [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    self.disappearProgress = 1.0;

}

- (void)updateDisappearAnimation {
    
    if (self.disappearProgress >= 0 && self.disappearProgress <= 1) {
        self.disappearProgress -= 1/60.f/kdisappearDuration;
        [self updateBarItemsWithProgress:self.disappearProgress];
        // 最后一個
        if (self.disappearProgress < (1/60.f/kdisappearDuration)) {
            [self.displayLink invalidate];
        }
    }
}


@end

#import <UIKit/UIKit.h>

@interface ShowLineItem : UIView


@property (nonatomic) CGFloat translationX;

// 初始化ItemView 的信息
- (instancetype)initWithFrame:(CGRect)frame startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint color:(UIColor *)color lineWidth:(CGFloat)lineWidth;
// 設置 frame
- (void)setupWithFrame:(CGRect)rect;
// 設置其生成隨機位置 或降到的地方
- (void)setHorizontalRandomness:(int)horizontalRandomness dropHeight:(CGFloat)dropHeight;

@end
#import "ShowLineItem.h"

@interface ShowLineItem ()

@property (nonatomic, assign) CGPoint middlePoint;
@property (nonatomic, assign) CGFloat lineWidth;
@property (nonatomic, assign, assign) CGPoint startPoint;
@property (nonatomic, assign) CGPoint endPoint;
@property (nonatomic, assign) UIColor *color;

@end

@implementation ShowLineItem

- (instancetype)initWithFrame:(CGRect)frame startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint color:(UIColor *)color lineWidth:(CGFloat)lineWidth {
    
    self = [super initWithFrame:frame];
    if (self) {
        _startPoint = startPoint;
        _endPoint = endPoint;
        _lineWidth = lineWidth;
        _color = color;
        
        // 獲取中心點
        CGPoint (^middlePoint)(CGPoint, CGPoint) = ^CGPoint(CGPoint a, CGPoint b) {
            CGFloat x = (a.x + b.x)/2.f;
            CGFloat y = (a.y + b.y)/2.f;
            return CGPointMake(x, y);
        };
        _middlePoint = middlePoint(startPoint, endPoint);
    }
    return self;
}

- (void)setupWithFrame:(CGRect)rectv {
    // 設置錨點
    self.layer.anchorPoint = CGPointMake(self.middlePoint.x/self.frame.size.width, self.middlePoint.y/self.frame.size.height);
    //  設置 Frame
    self.frame = CGRectMake(self.frame.origin.x + self.middlePoint.x - self.frame.size.width/2, self.frame.origin.y + self.middlePoint.y - self.frame.size.height/2, self.frame.size.width, self.frame.size.height);
}

- (void)setHorizontalRandomness:(int)horizontalRandomness dropHeight:(CGFloat)dropHeight
{
    // 設置剛開始的位置  x 隨機   y 由我們自己設定
    int randomNumber = - horizontalRandomness + arc4random()%horizontalRandomness*2;
    self.translationX = randomNumber;
    self.transform = CGAffineTransformMakeTranslation(self.translationX, -dropHeight);
}

- (void)drawRect:(CGRect)rect {
    // 畫出線來
    UIBezierPath* bezierPath = UIBezierPath.bezierPath;
    [bezierPath moveToPoint:self.startPoint];
    [bezierPath addLineToPoint:self.endPoint];
    [self.color setStroke];
    bezierPath.lineWidth = self.lineWidth;
    [bezierPath stroke];
}

@end

注意點1、形狀的確定

形狀的確定,也就是線位置的確定,而線位置是自己設置的,就是每一條線的起點和終點

NSArray *startPoints = @[@"{1,10}",@"{1,90}",@"{60,40}",@"{60,90}",@"{100,90}",@"{100,40}",@"{120,40}",@"{140,90}"];
NSArray *endPoints = @[@"{1,90}",@"{40,90}",@"{60,90}",@"{100,90}",@"{100,40}",@"{60,40}",@"{140,90}",@"{160,40}"];

自己根據自己要的形狀設置設置起始點,并設置 Frame:

for (ShowLineItem *lineItem in self.lineItems) {
      [lineItem setupWithFrame:self.frame];
}

然而背景 View的 frame 則是通過疊加起來的。

self.frame = CGRectMake(0, 0, width + 2, height + 2);

注意點2、顏色變亮

實際上就是透明度的改變,相當于就是由0.4 ==> 1.0 的亮度:

lineItem.alpha = 1.0f;
[lineItem.layer removeAllAnimations];
[UIView animateWithDuration:kloadingIndividualAnimationTiming animations:^{
        lineItem.alpha = kbarDarkAlpha; //kbarDarkAlpha == 0.4
    } completion:^(BOOL finished) {

}];

注意點3、飛走或飛進來

這個實際上就是 lineView 的 transform 的變化, 改變它就 OK 啦

// 設置剛開始的位置  x 隨機   y 由我們自己設定
self.transform = CGAffineTransformMakeTranslation(self.translationX, -dropHeight);

另外,錨點的確定和 CADisplayLink的使用看一下就 OK 啦,具體的實現或疑問可以多看看CBStoreHouseRefreshControl

最后祝各位七夕快樂,大家都有 LOVE,不讓其飛走啦!!! 2016-8-9

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 人生天地間,忽如遠行客。世間美色有三分,山西便得其二,一分杏花白,一分老酒香。 杏花村是典雅的,楊柳垂垂,燕鶯雙雙...
    納蘭蘇淺閱讀 223評論 0 0
  • 博客地址 1. 域名購買 域名就是網址,比如你輸入img421.com(一級域名),而我的博客blog.img42...
    Springer閱讀 2,955評論 4 20
  • 喵喵她是性格很開朗的女孩子,她有一個自小一塊長大的青梅竹馬,不過他們沒有發展成情侶,而是成了哥們。后來喵喵的哥們初...
    蘭奕閱讀 3,606評論 0 0
  • Day6 【靜心】練習緩吸和急吸 【動身】植樹去 【成長】相從心生 【助人】在學生群里發了個小紅包 【感恩】感恩學...
    石頭縫里的小嫩芽變大樹閱讀 170評論 0 1