iOS 動(dòng)畫(huà) —— LOVE

看了CBStoreHouseRefreshControl 效果筐眷,一直想模仿一下,先來(lái)感受下原GIF圖:

CBStoreHouseRefreshControl

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

loving_you

具體實(shí)現(xiàn)基本參照CBStoreHouseRefreshControl陈惰,只是將那個(gè)刷新方面的先抽離出來(lái)啦畦徘,這一塊具體的基本思路:

  • 畫(huà)單個(gè)線,并組成我們要的形狀
  • 讓線變成閃亮的抬闯,并讓線持續(xù)的閃亮
  • 最后讓所有的線飛起來(lái)

直接先看代碼實(shí)現(xiàn)吧:


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

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

@interface ShowView ()

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

@end

@implementation ShowView

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        
        // 通過(guò)位置確定我們要畫(huà)的
        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;
        }
        // 將我們這個(gè)View的Frame 重新擴(kuò)大一下 == 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);
        // 一進(jìn)入的默認(rèn)形式
        self.reverseLoadingAnimation = NO; // 閃亮的方向
        // 默認(rèn)已近完成好搭建
        [self updateBarItemsWithProgress:1.0];
        // 假設(shè)一開(kāi)始就動(dòng)畫(huà),后期去掉
        [self startAnimation];
        // 假設(shè) 在20 秒后 停止溶握,后期去掉
        [self performSelector:@selector(stopAnimation) withObject:nil afterDelay:20];
    }
    return self;
}

// 實(shí)際上設(shè)置
- (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 開(kāi)始之后的動(dòng)畫(huà)操作
- (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 結(jié)束之后的動(dòng)畫(huà)操作
- (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];
        // 最后一個(gè)
        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;
// 設(shè)置 frame
- (void)setupWithFrame:(CGRect)rect;
// 設(shè)置其生成隨機(jī)位置 或降到的地方
- (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;
        
        // 獲取中心點(diǎn)
        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 {
    // 設(shè)置錨點(diǎn)
    self.layer.anchorPoint = CGPointMake(self.middlePoint.x/self.frame.size.width, self.middlePoint.y/self.frame.size.height);
    //  設(shè)置 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
{
    // 設(shè)置剛開(kāi)始的位置  x 隨機(jī)   y 由我們自己設(shè)定
    int randomNumber = - horizontalRandomness + arc4random()%horizontalRandomness*2;
    self.translationX = randomNumber;
    self.transform = CGAffineTransformMakeTranslation(self.translationX, -dropHeight);
}

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

@end

注意點(diǎn)1杯缺、形狀的確定

形狀的確定,也就是線位置的確定睡榆,而線位置是自己設(shè)置的萍肆,就是每一條線的起點(diǎn)和終點(diǎn)

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}"];

自己根據(jù)自己要的形狀設(shè)置設(shè)置起始點(diǎn),并設(shè)置 Frame:

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

然而背景 View的 frame 則是通過(guò)疊加起來(lái)的胀屿。

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

注意點(diǎn)2塘揣、顏色變亮

實(shí)際上就是透明度的改變,相當(dāng)于就是由0.4 ==> 1.0 的亮度:

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

}];

注意點(diǎn)3碉纳、飛走或飛進(jìn)來(lái)

這個(gè)實(shí)際上就是 lineView 的 transform 的變化, 改變它就 OK 啦

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

另外,錨點(diǎn)的確定和 CADisplayLink的使用看一下就 OK 啦馏艾,具體的實(shí)現(xiàn)或疑問(wèn)可以多看看CBStoreHouseRefreshControl劳曹。

最后祝各位七夕快樂(lè),大家都有 LOVE琅摩,不讓其飛走啦L酢!房资! 2016-8-9

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蜕劝,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌岖沛,老刑警劉巖暑始,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異婴削,居然都是意外死亡廊镜,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)唉俗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嗤朴,“玉大人,你說(shuō)我怎么就攤上這事虫溜”㈡ⅲ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵衡楞,是天一觀的道長(zhǎng)吱雏。 經(jīng)常有香客問(wèn)我,道長(zhǎng)寺酪,這世上最難降的妖魔是什么坎背? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮寄雀,結(jié)果婚禮上得滤,老公的妹妹穿的比我還像新娘。我一直安慰自己盒犹,他們只是感情好懂更,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著急膀,像睡著了一般沮协。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卓嫂,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天慷暂,我揣著相機(jī)與錄音,去河邊找鬼晨雳。 笑死行瑞,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的餐禁。 我是一名探鬼主播血久,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼帮非!你這毒婦竟也來(lái)了氧吐?” 一聲冷哼從身側(cè)響起讹蘑,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎筑舅,沒(méi)想到半個(gè)月后座慰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡豁翎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年角骤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片心剥。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡邦尊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出优烧,到底是詐尸還是另有隱情蝉揍,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布畦娄,位于F島的核電站又沾,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏熙卡。R本人自食惡果不足惜杖刷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望驳癌。 院中可真熱鬧滑燃,春花似錦、人聲如沸颓鲜。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)甜滨。三九已至乐严,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間衣摩,已是汗流浹背昂验。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留艾扮,地道東北人既琴。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像栏渺,于是被迫代替她去往敵國(guó)和親呛梆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锐涯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 人生天地間磕诊,忽如遠(yuǎn)行客。世間美色有三分,山西便得其二霎终,一分杏花白滞磺,一分老酒香。 杏花村是典雅的莱褒,楊柳垂垂击困,燕鶯雙雙...
    納蘭蘇淺閱讀 200評(píng)論 0 0
  • 博客地址 1. 域名購(gòu)買(mǎi) 域名就是網(wǎng)址,比如你輸入img421.com(一級(jí)域名),而我的博客blog.img42...
    Springer閱讀 2,925評(píng)論 4 20
  • 喵喵她是性格很開(kāi)朗的女孩子,她有一個(gè)自小一塊長(zhǎng)大的青梅竹馬广凸,不過(guò)他們沒(méi)有發(fā)展成情侶阅茶,而是成了哥們。后來(lái)喵喵的哥們初...
    蘭奕閱讀 3,576評(píng)論 0 0
  • Day6 【靜心】練習(xí)緩吸和急吸 【動(dòng)身】植樹(shù)去 【成長(zhǎng)】相從心生 【助人】在學(xué)生群里發(fā)了個(gè)小紅包 【感恩】感恩學(xué)...
    石頭縫里的小嫩芽變大樹(shù)閱讀 163評(píng)論 0 1