废话开篇:通过 UIView 绘制方法实现手机充电动画
一、实现效果
二、步骤分析
实现充电效果分为两个部分:
1、顶部能量积聚部分
展示原理:
利用多椭圆错位重叠旋转。绘制三个不同颜色的椭圆,并分别赋予三者不同的颜色、尺寸及初始旋转角度。在同时进行自旋即可产生上图所示效果。
2、底部吸附能量部分
展示原理:
通过上下文的绘制,将圆与贝塞尔曲线相结合,实现吸附效果。当然,这里面也分为不同的运动阶段,来区别贝塞尔曲线的绘制条件,整体上看上去是被吸附上来。
三、代码展示
1、椭圆 BubbleEllipseView 绘制
@interface BubbleEllipseView : UIView
@property(nonatomic,strong) UIColor * color;
@end
@implementation BubbleEllipseView
- (instancetype)initWithFrame:(CGRect)frame duration:(CFTimeInterval)duration color:(UIColor *)color rotate:(CGFloat)rotate
{
if (self = [super initWithFrame:frame]) {
if (rotate != 0) {
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformRotate(transform, rotate);
self.transform = transform;
}
self.backgroundColor = [UIColor clearColor];
self.color = color;
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
animation.toValue = @(2*M_PI + rotate);
animation.duration = duration;
animation.removedOnCompletion = false;
animation.repeatCount = MAXFLOAT;
[self.layer addAnimation:animation forKey:nil];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context =UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, self.color.CGColor);
//椭圆
CGContextAddEllipseInRect(context,self.bounds);//椭圆
CGContextDrawPath(context,kCGPathFill);
}
@end
复制代码
2、圆 BubbleCircleView 绘制
@interface BubbleCircleView : UIView
@property(nonatomic,strong) UIColor * color;
@end
@implementation BubbleCircleView
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context =UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextAddArc(context,rect.size.width / 2.0,rect.size.height / 2.0,rect.size.height / 2.0,0,2*M_PI,0);//添加一个圆
CGContextDrawPath(context,kCGPathFill);//绘制路径加填充
}
复制代码
3、吸附能量条 SmallBubbleView 绘制
为了满足动画效果,这里代码比较多。对于各个运动阶段采取不同的绘制效果。
单束:
多束:
@interface SmallBubbleView : UIView
@property(nonatomic,assign) CGFloat startTime;
@property(nonatomic,strong) UIColor * color;
@property(nonatomic,strong) NSArray * colorsArr;
@property(nonatomic,assign) CGFloat bubbleRadius;
@property(nonatomic,assign) CGPoint bubbleCenter;
@property(nonatomic,assign) CGFloat radian;
@property(nonatomic,assign) CGFloat maxAbsorbLength;
@property(nonatomic,assign) CGFloat maxRadius;
@property(nonatomic,assign) BubbleLifeCircleType bubbleLifeCircleType;
@end
@implementation SmallBubbleView
- (instancetype)initWithFrame:(CGRect)frame startTime:(CGFloat)startTime
{
if (self = [super initWithFrame:frame]) {
self.startTime = startTime;
self.backgroundColor = [UIColor clearColor];
self.colorsArr = @[[UIColor colorWithRed:113 / 255.0 green:191 / 255.0 blue:188 / 255.0 alpha:0.8],[UIColor colorWithRed:160 / 255.0 green:170 / 255.0 blue:229 / 255.0 alpha:1],[UIColor colorWithRed:48 / 255.0 green:116 / 255.0 blue:195 / 255.0 alpha:0.8]];
self.color = self.colorsArr[arc4random() % self.colorsArr.count];
self.maxAbsorbLength = 63;
self.maxRadius = self.frame.size.width / 2.5;
self.bubbleCenter = CGPointMake(frame.size.width / 2.0, frame.size.height);
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
switch (self.bubbleLifeCircleType) {
case BubbleLifeCircleCreate:
{
CGContextSetFillColorWithColor(context, self.color.CGColor);
CGContextAddArc(context,self.bubbleCenter.x,self.bubbleCenter.y,self.bubbleRadius,0,2*M_PI,0);//添加一个圆
CGContextDrawPath(context,kCGPathFill);//绘制路径加填充
}
break;
case BubbleLifeCircleStartAbsorb:
case BubbleLifeCircleAbsorbEnough:
{
//圆
CGContextSetFillColorWithColor(context, self.color.CGColor);
CGContextAddArc(context,self.bubbleCenter.x,self.bubbleCenter.y,self.bubbleRadius,0,2*M_PI,0);//添加一个圆
//贝塞尔曲线
UIBezierPath * bezierPath = [self getAbsorbBezierPath];
CGContextAddPath(context, bezierPath.CGPath);
CGContextDrawPath(context,kCGPathFill);//绘制路径加填充
}
break;
case BubbleLifeCircleMove:
{
//圆
CGContextSetFillColorWithColor(context, self.color.CGColor);
CGContextAddArc(context,self.bubbleCenter.x,self.bubbleCenter.y,self.bubbleRadius,0,2*M_PI,0);//添加一个圆
//贝塞尔曲线
UIBezierPath * bezierPath = [self getAbsorbBezierPath];
CGContextAddPath(context, bezierPath.CGPath);
CGContextDrawPath(context,kCGPathFill);//绘制路径加填充
}
break;
case BubbleLifeCircleEnd:
{
}
break;
default:
break;
}
}
//获取吸附状态贝塞尔曲线
- (UIBezierPath *)getAbsorbBezierPath
{
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
//圆弧左点
self.radian += 0.01;
self.radian = self.radian > M_PI_4 ? M_PI_4 : self.radian;
CGFloat leftCircleX = self.bubbleCenter.x - self.bubbleRadius * sin(self.radian);
CGFloat leftCircleY = self.bubbleCenter.y + self.bubbleRadius * cos(self.radian);
CGPoint leftCirclePoint = CGPointMake(leftCircleX, leftCircleY);
//圆弧右点
CGFloat rightCircleX = self.bubbleCenter.x + self.bubbleRadius * sin(self.radian);
CGFloat rightCircleY = self.bubbleCenter.y + self.bubbleRadius * cos(self.radian);
CGPoint rightCirclePoint = CGPointMake(rightCircleX, rightCircleY);
switch (self.bubbleLifeCircleType) {
case BubbleLifeCircleStartAbsorb:
{
[bezierPath moveToPoint:leftCirclePoint];
[bezierPath addArcWithCenter:self.bubbleCenter radius:self.bubbleRadius startAngle:M_PI_2 + self.radian endAngle:M_PI_2 - self.radian clockwise:false];
[bezierPath moveToPoint:rightCirclePoint];
[bezierPath addLineToPoint:CGPointMake(self.bubbleCenter.x, self.frame.size.height)];
[bezierPath addLineToPoint:leftCirclePoint];
}
break;
case BubbleLifeCircleAbsorbEnough:
{
[bezierPath moveToPoint:leftCirclePoint];
[bezierPath addArcWithCenter:self.bubbleCenter radius:self.bubbleRadius startAngle:M_PI_2 + self.radian endAngle:M_PI_2 - self.radian clockwise:false];
[bezierPath moveToPoint:rightCirclePoint];
[bezierPath addQuadCurveToPoint:CGPointMake(self.bubbleCenter.x, self.frame.size.height) controlPoint:CGPointMake(self.bubbleCenter.x, self.bubbleCenter.y + (self.frame.size.height - self.bubbleCenter.y) / 2.0)];
[bezierPath addQuadCurveToPoint:leftCirclePoint controlPoint:CGPointMake(self.bubbleCenter.x, self.bubbleCenter.y + (self.frame.size.height - self.bubbleCenter.y) / 2.0)];
}
break;
case BubbleLifeCircleMove:
{
[bezierPath moveToPoint:leftCirclePoint];
[bezierPath addArcWithCenter:self.bubbleCenter radius:self.bubbleRadius startAngle:M_PI_2 + self.radian endAngle:M_PI_2 - self.radian clockwise:false];
[bezierPath moveToPoint:rightCirclePoint];
CGPoint bottomPoint = CGPointMake(self.bubbleCenter.x, self.bubbleCenter.y + self.maxRadius + self.maxAbsorbLength / 2.0);
[bezierPath addQuadCurveToPoint:bottomPoint controlPoint:CGPointMake(self.bubbleCenter.x,self.bubbleCenter.y + (bottomPoint.y - self.bubbleCenter.y) / 2.0)];
[bezierPath addQuadCurveToPoint:leftCirclePoint controlPoint:CGPointMake(self.bubbleCenter.x, self.bubbleCenter.y + (bottomPoint.y - self.bubbleCenter.y) / 2.0)];
}
break;
default:
break;
}
return bezierPath;
}
//重绘
- (void)refreshDraw
{
CGFloat proportion = 10.0;
switch (self.bubbleLifeCircleType) {
case BubbleLifeCircleCreate:
{
self.bubbleRadius += (0.05 * proportion);
self.bubbleRadius = self.bubbleRadius > self.maxRadius ? self.maxRadius : self.bubbleRadius;
if (self.frame.size.height - self.bubbleCenter.y - self.maxRadius > 0) {
self.bubbleLifeCircleType = BubbleLifeCircleStartAbsorb;
}
self.bubbleCenter = CGPointMake(self.bubbleCenter.x, self.bubbleCenter.y - (0.05 * proportion));
}
break;
case BubbleLifeCircleStartAbsorb:
{
self.bubbleCenter = CGPointMake(self.bubbleCenter.x, self.bubbleCenter.y - (0.04 * proportion));
if (self.frame.size.height - self.bubbleCenter.y - self.maxRadius > 23) {
self.bubbleLifeCircleType = BubbleLifeCircleAbsorbEnough;
}
}
break;
case BubbleLifeCircleAbsorbEnough:
{
self.bubbleCenter = CGPointMake(self.bubbleCenter.x, self.bubbleCenter.y - (0.06 * proportion));
if (self.frame.size.height - self.bubbleCenter.y - self.maxRadius > self.maxAbsorbLength) {
self.bubbleLifeCircleType = BubbleLifeCircleMove;
}
}
break;
case BubbleLifeCircleMove:
{
self.bubbleCenter = CGPointMake(self.bubbleCenter.x, self.bubbleCenter.y - (0.06 * proportion));
if (self.bubbleCenter.y < 0) {
self.bubbleLifeCircleType = BubbleLifeCircleEnd;
}
}
break;
case BubbleLifeCircleEnd:
{
self.bubbleCenter = CGPointMake(self.frame.size.width / 2.0, self.frame.size.height);
self.bubbleRadius = 0;
self.color = self.colorsArr[arc4random() % self.colorsArr.count];
self.bubbleLifeCircleType = BubbleLifeCircleCreate;
}
break;
default:
break;
}
[self setNeedsDisplay];
}
@end
复制代码
4、整体 BubbleView 绘制
全部绘制到 BubbleView 视图上,实现整体效果
@interface BubbleView()
@property(nonatomic,strong) NSTimer * timer;
@property(nonatomic,strong) NSMutableArray * smallBubbleSaveArr;
@end
@implementation BubbleView
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.smallBubbleSaveArr = [[NSMutableArray alloc] init];
self.backgroundColor = [UIColor blackColor];
CGPoint center = CGPointMake(frame.size.width / 2.0, frame.size.height / 2.0);
CGFloat width = 150;
//椭圆绘制
BubbleEllipseView * ellipseView = [[BubbleEllipseView alloc] initWithFrame:CGRectMake(0, 0, width, width + 22) duration:1.6 color:[UIColor colorWithRed:160 / 255.0 green:170 / 255.0 blue:229 / 255.0 alpha:1] rotate:0];
ellipseView.center = center;
BubbleEllipseView * ellipseView1 = [[BubbleEllipseView alloc] initWithFrame:CGRectMake(0, 0, width + 15, width) duration:1.5 color:[UIColor colorWithRed:113 / 255.0 green:191 / 255.0 blue:188 / 255.0 alpha:1] rotate:0];
ellipseView1.center = center;
BubbleEllipseView * ellipseView2 = [[BubbleEllipseView alloc] initWithFrame:CGRectMake(0, 0, width + 25, width - 10) duration:2.5 color:[UIColor colorWithRed:48 / 255.0 green:116 / 255.0 blue:195 / 255.0 alpha:0.8] rotate:M_PI / 4];
ellipseView2.center = center;
//圆绘制
BubbleCircleView * circleView = [[BubbleCircleView alloc] initWithFrame:CGRectMake(0, 0, width - 5, width - 5)];
circleView.center = center;
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.005 target:self selector: **@selector**(absorbEnergy) userInfo:**nil** repeats:YES];
[self.timer fire];
CGFloat smallBubbleWidth = 30;
CGFloat smallBubbleHeight = frame.size.height - CGRectGetMaxY(circleView.frame);
//吸附能量左、中、右绘制
SmallBubbleView * smallBubbleView = [[SmallBubbleView alloc] initWithFrame:CGRectMake((frame.size.width - smallBubbleWidth) / 2.0, frame.size.height - smallBubbleHeight - 30, smallBubbleWidth, smallBubbleHeight) startTime:0];
[self.smallBubbleSaveArr addObject:smallBubbleView];
SmallBubbleView * smallBubbleLeftView = [[SmallBubbleView alloc] initWithFrame:CGRectMake((frame.size.width - smallBubbleWidth) / 2.0 - smallBubbleWidth, frame.size.height - smallBubbleHeight - 30, smallBubbleWidth, smallBubbleHeight) startTime:2];
[self.smallBubbleSaveArr addObject:smallBubbleLeftView];
SmallBubbleView * smallBubbleRightView = [[SmallBubbleView alloc] initWithFrame:CGRectMake((frame.size.width - smallBubbleWidth) / 2.0 + smallBubbleWidth, frame.size.height - smallBubbleHeight - 30, smallBubbleWidth, smallBubbleHeight) startTime:1.5];
[self.smallBubbleSaveArr addObject:smallBubbleRightView];
//添加子视图
[self addSubview:smallBubbleLeftView];
[self addSubview:smallBubbleView];
[self addSubview:smallBubbleRightView];
[self addSubview:ellipseView2];
[self addSubview:ellipseView1];
[self addSubview:ellipseView];
[self addSubview:circleView];
}
return self;
}
//开始吸附动画
- (void)absorbEnergy
{
for (SmallBubbleView * smallBubbleView in self.smallBubbleSaveArr) {
//延迟刷新,交错效果
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(smallBubbleView.startTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[smallBubbleView refreshDraw];
});
}
}
//停止定时器
- (void)stop
{
[self.timer invalidate];
self.timer = nil;
}
@end
复制代码
四、总结与思考
简单的实现充电效果,代码拙劣,大神勿笑[抱拳][抱拳][抱拳]