今天带来的是仿百度外卖个人中心的波浪效果,先看下效果图吧
一,项目里用到了两个类:
一个是CADisplayLink类,我用它取代了NSTimer,首先,它是利用刷帧和屏幕频率一样来重绘渲染页面,也就是说每次屏幕刷新的时候就会调用它的响应方法(屏幕一般一秒刷新60次)在绘图中需要重绘时常用它来代替NSTimer,其次,因为NSTimer调度优先级比较低,并不会准时调用,做动画的话会有卡顿的感觉
另一个是CAShapeLayer类,它属于CALayer的子类,通常结合CGPath来绘制不规则矩形图形.
其优点:
1.渲染效率高渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
2.高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。
3.不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉。
4.不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。
二,下面是功能的实现(直接使用就可以)
先自定义类,基于 UIView(WavesView)
//
// WavesView.h
// 仿百度外卖-个人中心(头像波浪效果)
//
// Created by Amydom on 16/12/28.
// Copyright © 2016年 Amydom. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef void(^WavesBlock)(CGRect myFrame);
@interface WavesView : UIView
/**
* 浪弯曲度
*/
@property (nonatomic, assign) CGFloat waveCurvature;
/**
* 浪速
*/
@property (nonatomic, assign) CGFloat waveSpeed;
/**
* 浪高
*/
@property (nonatomic, assign) CGFloat waveHeight;
/**
* 实浪颜色
*/
@property (nonatomic, strong) UIColor *realWaveColor;
/**
* 遮罩浪颜色
*/
@property (nonatomic, strong) UIColor *maskWaveColor;
@property (nonatomic, copy) WavesBlock waveBlock;
@property (nonatomic , assign)CGRect imageFrame;
- (void)stopWaveAnimation;
- (void)startWaveAnimation;
@end
//
// WavesView.m
// 仿百度外卖-个人中心(头像波浪效果)
//
// Created by Amydom on 16/12/28.
// Copyright © 2016年 Amydom. All rights reserved.
//
#import "WavesView.h"
@interface WavesView ()
/**
* 定时器
CADisplayLink:利用刷帧和屏幕频率一样来重绘渲染页面,也就是说每次屏幕刷新的时候就会调用它的响应方法(屏幕一般一秒刷新60次),在绘图中需要重绘时常用它来代替NSTimer,因为NSTimer调度优先级比较低,并不会准时调用,做动画的话会有卡顿的感觉
*/
@property (nonatomic, strong) CADisplayLink *timer;
/**
* 实浪动画
CAShapeLayer:CALayer的子类,通常结合CGPath来绘制不规则矩形图形
其优点:
1.渲染效率高渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
2.高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。
3.不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉。
4.不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。
*/
@property (nonatomic, strong) CAShapeLayer *realWaveLayer;
/**
* 遮罩浪动画
*/
@property (nonatomic, strong) CAShapeLayer *maskWaveLayer;
/**
* <#Description#>
*/
@property (nonatomic, assign) CGFloat offset;
@end
@implementation WavesView
#pragma mark - 初始化
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setUp];
}
return self;
}
-(void)setUp{
//初始化
self.waveSpeed = 0.5;
self.waveCurvature = 1.5;
self.waveHeight = 4;
self.realWaveColor = [UIColor whiteColor];
self.maskWaveColor = [[UIColor whiteColor] colorWithAlphaComponent:0.4];
[self.layer addSublayer:self.realWaveLayer];
[self.layer addSublayer:self.maskWaveLayer];
}
#pragma mark - lazyload
- (CAShapeLayer *)realWaveLayer{
if (!_realWaveLayer) {
_realWaveLayer = [CAShapeLayer layer];
CGRect frame = self.bounds;
frame.origin.y = frame.size.height-self.waveHeight;
frame.size.height = self.waveHeight;
_realWaveLayer.frame = frame;
_realWaveLayer.fillColor = self.realWaveColor.CGColor;
}
return _realWaveLayer;
}
- (CAShapeLayer *)maskWaveLayer{
if (!_maskWaveLayer) {
_maskWaveLayer = [CAShapeLayer layer];
CGRect frame = self.bounds;
frame.origin.y = frame.size.height-self.waveHeight;
frame.size.height = self.waveHeight;
_maskWaveLayer.frame = frame;
_maskWaveLayer.fillColor = self.maskWaveColor.CGColor;
}
return _maskWaveLayer;
}
- (void)setWaveHeight:(CGFloat)waveHeight{
_waveHeight = waveHeight;
CGRect frame = self.bounds;
frame.origin.y = frame.size.height - self.waveHeight;
frame.size.height = self.waveHeight;
_realWaveLayer.frame = _maskWaveLayer.frame = frame;
}
#pragma mark - 动画
- (void)startWaveAnimation{
self.timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(wave)];
[self.timer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)stopWaveAnimation{
[self.timer invalidate];
self.timer = nil;
}
//描述路径,并用CAShapeLayer绘制出来
- (void)wave{
self.offset += self.waveSpeed;
//获取宽,高
CGFloat width = CGRectGetWidth(self.frame);
CGFloat height = self.waveHeight;
//真实波浪
CGMutablePathRef realpath = CGPathCreateMutable();
CGPathMoveToPoint(realpath, NULL, 0, height);
CGFloat realY = 0.f;
//遮罩波浪
CGMutablePathRef maskpath = CGPathCreateMutable();
CGPathMoveToPoint(maskpath, NULL, 0, height);
CGFloat maskY = 0.f;
for (CGFloat x = 0.f; x <= width; x++) {
realY = height * sinf(0.01 * self.waveCurvature * x + self.offset * 0.045);
CGPathAddLineToPoint(realpath, NULL, x, realY);
maskY = -realY;
CGPathAddLineToPoint(maskpath, NULL, x, maskY);
}
//变化的中间 Y 值
CGFloat centX = self.bounds.size.width / 2;
CGFloat centY = height * sinf(0.01 * self.waveCurvature * centX + self.offset * 0.045);
if (self.waveBlock) {
//修改头像view的高度
CGRect iconFrame = self.imageFrame;
iconFrame.origin.y = CGRectGetHeight(self.frame)-CGRectGetHeight(self.imageFrame) + centY - self.waveHeight;
self.imageFrame = iconFrame;
self.waveBlock(self.imageFrame);
}
//真实波浪
CGPathAddLineToPoint(realpath, NULL, width, height);
CGPathAddLineToPoint(realpath, NULL, 0, height);
CGPathCloseSubpath(realpath);
//描述路径后利用CAShapeLayer类绘制不规则图形
self.realWaveLayer.path = realpath;
self.realWaveLayer.fillColor = self.realWaveColor.CGColor;
CGPathRelease(realpath);
//遮罩波浪
CGPathAddLineToPoint(maskpath, NULL, width, height);
CGPathAddLineToPoint(maskpath, NULL, 0, height);
CGPathCloseSubpath(maskpath);
//描述路径后利用CAShapeLayer类绘制不规则图形
self.maskWaveLayer.path = maskpath;
self.maskWaveLayer.fillColor = self.maskWaveColor.CGColor;
CGPathRelease(maskpath);
}
@end
然后是调用,这里就简单的说明下如何调用(在 viewController 里)
首先创建的头像的 image(我是通过懒加载创建的)
- (UIImageView *)iconImageView{
if (!_iconImageView) {
_iconImageView = [[UIImageView alloc] initWithFrame:CGRectMake(self.WavesView.frame.size.width/2 - 30, 0, 60, 60)];
_iconImageView.layer.borderColor = [UIColor whiteColor].CGColor;
_iconImageView.layer.borderWidth = 2;
_iconImageView.layer.cornerRadius = 30;
}
return _iconImageView;
}
然后创建 wavesView,并且把创建好的 image的 frame 传过去
- (void)viewDidLoad {
[super viewDidLoad];
//创建对线
self.WavesView = [[WavesView alloc]initWithFrame:CGRectMake(0, 22, self.view.frame.size.width, 150)];
self.WavesView.backgroundColor =[UIColor redColor];
[self.view addSubview:self.WavesView];
[_WavesView addSubview:self.iconImageView];
//这里把头像的 frame 传入到 wavesView里,让其通过 frame来计算该变量
_WavesView.imageFrame = _iconImageView.frame;
//防止循环引用
__weak typeof(self)weakSelf = self;
_WavesView.waveBlock = ^(CGRect imageFrame){
//修改头像view的frame(时时的通过 block 回调改变的 frame)
weakSelf.iconImageView.frame = imageFrame;
};
//开始执行
[_WavesView startWaveAnimation];
}
到这里,波浪形动画就完成了.....大家没事可以多看看动画的实现,对于里面有些东西我也是比较模糊的状态,但是多看多谢,慢慢的你就会发现收获还是有的...