看了CBStoreHouseRefreshControl 效果筐眷,一直想模仿一下,先來(lái)感受下原GIF圖:
然后想著今天七夕,于是也模仿這這個(gè)想寫(xiě)一個(gè) Love 的組成,但是單身的我貌似不好寫(xiě) LOVE诚纸, 就讓 LOVE 少一個(gè) E 吧!
具體實(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