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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,475评论 4 372
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,744评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,101评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,732评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,141评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,049评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,188评论 2 320
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,965评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,716评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,867评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,341评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,663评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,376评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,200评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,990评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,179评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,979评论 2 279

推荐阅读更多精彩内容

  • 人生天地间,忽如远行客。世间美色有三分,山西便得其二,一分杏花白,一分老酒香。 杏花村是典雅的,杨柳垂垂,燕莺双双...
    纳兰苏浅阅读 187评论 0 0
  • 博客地址 1. 域名购买 域名就是网址,比如你输入img421.com(一级域名),而我的博客blog.img42...
    Springer阅读 2,854评论 4 20
  • 喵喵她是性格很开朗的女孩子,她有一个自小一块长大的青梅竹马,不过他们没有发展成情侣,而是成了哥们。后来喵喵的哥们初...
    兰奕阅读 3,530评论 0 0
  • Day6 【静心】练习缓吸和急吸 【动身】植树去 【成长】相从心生 【助人】在学生群里发了个小红包 【感恩】感恩学...
    石头缝里的小嫩芽变大树阅读 151评论 0 1