看了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