话说又有这么一个需求……产品想让项目中增加一个按钮:一个点击了之后会有波纹上升的按钮。在波纹到达顶端的时候还会有三个泡泡出现,以随机的路线不同的速度漂浮到固定的一个点……
好吧,你赢了。
先来完成这个波纹按钮
/*
正弦函数
y =Asin(ωx+φ)+C
A 表示振幅,也就是使用这个变量来调整波浪的高度
ω表示周期,也就是使用这个变量来调整在屏幕内显示的波浪的数量
φ表示波浪横向的偏移,也就是使用这个变量来调整波浪的流动
C表示波浪纵向的位置,也就是使用这个变量来调整波浪在屏幕中竖直的位置。
*/
{
UIImage *_img;
BOOL _once;
}
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) CADisplayLink *waveDisplaylink;
@property (nonatomic, strong) CAShapeLayer *firstWaveLayer;
@property (nonatomic, strong) CAShapeLayer *secondWaveLayer;
@property (nonatomic) CGFloat waveA; //水纹振幅 表示上面的A
@property (nonatomic) CGFloat waveW;//水纹周期 表示上面的ω
@property (nonatomic) CGFloat offsetX; //位移 表示上面的φ
@property (nonatomic) CGFloat currentK; //当前波浪高度Y 表示上面的C
@property (nonatomic) CGFloat waveSpeed;//水纹速度 表示波浪流动的速度
@property (nonatomic) CGFloat waterWaveWidth; //水纹宽度
@property (nonatomic, strong) UIColor *firstWaveColor; //波浪的颜色
属性大致就是这些。当按钮被点击的时候显示波浪
//按钮被点击
-(void)viewDidClicked
{
//动画效果下只能点击一次
if (_once) {
return;
}
if (self.animation) {
//创建波浪
_once = YES;
[self createWave];
}else{
//回调
if (self.buttonDidClick) {
self.buttonDidClick();
}
if ([self.delegate respondsToSelector:@selector(waveButtonDidClicked)]) {
[self.delegate waveButtonDidClicked];
}
}
}
#pragma mark - 创建波浪
-(void)createWave{
//设置波浪的宽度
self.waterWaveWidth = self.bounds.size.width;
//设置波浪的颜色
self.firstWaveColor = RGBA(250, 188, 16, 1);
//设置波浪的速度
self.waveSpeed = 0.4/M_PI;
//初始化layer
if (_firstWaveLayer == nil) {
//初始化
_firstWaveLayer = [CAShapeLayer layer];
//设置闭环的颜色
_firstWaveLayer.fillColor = _firstWaveColor.CGColor;
[self.layer addSublayer:_firstWaveLayer];
}
//这里设置两个layer叠加起来丰富层次
if (self.secondWaveLayer == nil) {
//初始化
self.secondWaveLayer = [CAShapeLayer layer];
//设置闭环的颜色
self.secondWaveLayer.fillColor = _firstWaveColor.CGColor;
[self.layer addSublayer:self.secondWaveLayer];
}
[self bringSubviewToFront:self.titleLabel];
[self bringSubviewToFront:self.imageView];
//设置波浪流动速度
self.waveSpeed = 0.5;
//设置振幅
self.waveA = 2;
//设置周期
self.waveW = 1/20.0;
//设置波浪纵向位置
self.currentK = self.frame.size.height;//屏幕居中
//启动定时器
self.waveDisplaylink = [CADisplayLink displayLinkWithTarget:self selector:@selector(getCurrentWave:)];
[self.waveDisplaylink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
//定时器调用的方法
-(void)getCurrentWave:(CADisplayLink *)displayLink
{
//实时的位移
self.offsetX += self.waveSpeed;
if (self.currentK > 0) {
self.currentK --;
//使波浪动起来
[self setCurrentFirstWaveLayerPath];
[self setCurrentSecondWaveLayerPath];
} else {
//波浪到达了顶点
self.backgroundColor = self.firstWaveColor;
//移除两个波浪层
[self.firstWaveLayer removeFromSuperlayer];
[self.secondWaveLayer removeFromSuperlayer];
//销毁计时器
[_waveDisplaylink invalidate];
//回调
if (self.buttonDidClick) {
self.buttonDidClick();
}
if ([self.delegate respondsToSelector:@selector(waveButtonDidClicked)]) {
[self.delegate waveButtonDidClicked];
}
}
}
//创建波浪路径,其实这两个方法也可以写成一个通过参数调整的方法,不过当时懒……
-(void)setCurrentFirstWaveLayerPath
{
//创建一个路径
UIBezierPath *path = [UIBezierPath new];
CGFloat y = self.currentK;
//将点移动到 x=0,y=currentK的位置
[path moveToPoint:CGPointMake(0, y)];
for (NSInteger x = 0.0f; x<=self.waterWaveWidth; x++) {
//正玄波浪公式
y = self.waveA * sin(self.waveW * x + self.offsetX)+self.currentK;
//将点连成线
[path addLineToPoint:CGPointMake(x, y)];
}
[path addLineToPoint:CGPointMake(self.waterWaveWidth, self.frame.size.height)];
[path addLineToPoint:CGPointMake(0, self.frame.size.height)];
self.firstWaveLayer.path = path.CGPath;
}
-(void)setCurrentSecondWaveLayerPath
{
//创建一个路径
UIBezierPath *path = [UIBezierPath new];
CGFloat y = self.currentK;
//将点移动到 x=0,y=currentK的位置
[path moveToPoint:CGPointMake(0, y)];
for (NSInteger x = 0.0f; x<=self.waterWaveWidth; x++) {
//正玄波浪公式
y = (self.waveA+2) * sin((self.waveW) * x - self.offsetX + 10)+self.currentK;
//将点连成线
[path addLineToPoint:CGPointMake(x, y)];
}
[path addLineToPoint:CGPointMake(self.waterWaveWidth, self.frame.size.height)];
[path addLineToPoint:CGPointMake(0, self.frame.size.height)];
self.secondWaveLayer.path = path.CGPath;
}
这样带有波浪的按钮就完成了。继续做按钮完成时出现的泡泡。
//泡泡视图的初始化方法,参数是泡泡的起始点和终点
-(instancetype)initWithFrame:(CGRect)frame startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor clearColor];
self.startPoint = startPoint;
self.endPoint = endPoint;
//泡泡是默认隐藏的
[self addSubview:self.bubble1];
[self addSubview:self.bubble2];
[self addSubview:self.bubble3];
[self initPath];
_bubbleArray = @[self.bubble1, self.bubble2, self.bubble3];
}
return self;
}
//创建路径
-(void)initPath{
for (int i=1; i<4; i++) {
//第一条路径
UIBezierPath *path = [UIBezierPath bezierPath];
//移动到起点
[path moveToPoint:self.startPoint];
//随机一个控制点,x最大是屏幕宽-小球的宽度,这样才能显示小球
CGFloat x1 = arc4random_uniform(ScreenWidth/2)+ScreenWidth/2-24;
//转折点在起点终点中间位置,上下波动不超过30
CGFloat y1 = arc4random_uniform(30)+(self.startPoint.y-self.endPoint.y)/2;
//添加控制点
[path addQuadCurveToPoint:self.endPoint controlPoint:CGPointMake(x1, y1)];
//设置曲线类型
path.lineJoinStyle = kCGLineJoinRound;
if (i==1) {
self.path1 = path;
}else if (i==2){
self.path2 = path;
}else{
self.path3 = path;
}
}
_pathArray = @[self.path1, self.path2, self.path3];
}
这样泡泡和路径都设置好了,只要在波纹按钮升到定点的回调方法中调用泡泡视图的show。
-(void)show{
for (int i=0; i<_bubbleArray.count; i++) {
UIImageView *bubble = _bubbleArray[i];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//设置路径
animation.path = [_pathArray[i] CGPath];
//设置重复次数
animation.repeatCount = 1;
//动画时长
animation.duration = 1+i*0.25;
animation.delegate = self;
//完成后不移除
animation.removedOnCompletion = NO;
//
animation.fillMode = kCAFillModeForwards;
[animation setValue:@(i) forKey:@"count"];
//添加动画
bubble.alpha = 1;
[bubble.layer addAnimation:animation forKey:@""];
}
}
最后在动画结束的协议方法中将泡泡视图隐藏掉:
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
if (flag) {
NSInteger count = [[anim valueForKey:@"count"] integerValue];
UIImageView *bubble = _bubbleArray[count];
bubble.alpha = 0;
if (count == 2) {
//回调
if (self.animationDidEnd) {
self.animationDidEnd();
}
}
}
}
就完成啦!