参考贴连接
这里总结一下
1.核心公式
正弦型函数解析式:y=Asin(ωx+φ)+h
φ(初相位):决定波形与X轴位置关系或横向移动距离(左加右减)
ω:决定周期(最小正周期T=2π/|ω|)
A:决定峰值(即纵向拉伸压缩的倍数)
h:表示波形在Y轴的位置关系或纵向移动距离(上加下减)
以上的公式一开始看不懂没关系, 参数看不懂也没关系
我们就拿我们知道的波峰/波宽/震幅等好理解的属性来设置
//我们需要两个shapelayer
CAShapeLayer *waveLayer; //水波浪1
CAShapeLayer *waveLayerTwo; //水波浪2
//以及相关属性
CGFloat speed; //沿X轴波峰移动速度
CGFloat offsetX; //沿X轴波峰移动偏移量
CGFloat offsetY; //沿Y轴波起点位置(即公式中的h)
CGFloat waveWidth; //水波浪总宽度
CGFloat A; //上下的振幅
1.speed : 这个决定了在UIkit坐标系下,水波浪线的point点的X坐标沿着X轴移动的速度
2.offsetX 这个决定了在UIkit坐标系下,水波浪线的point点在X轴上的位移(offsetX随着每秒60帧的CADisplayLink调用,会以speed为单位递增 offsetX += speed);
3.offsetY 这个决定了在UIkit坐标系下,水波浪线的point点在Y轴上的初始位置,说白了就是决定这个水波浪layer的整体(包括水波浪线和下面的矩形)高度
4 waveWidth 总宽度 ,这个没什么好说的
5.A 就是公式中的A 决定了振幅的大小,也就是波峰波谷上下震动的效果量
开始套用公式
CGFloat y;
CGFloat ω = 1.1*M_PI/waveWidth;
CGFloat φ = offsetX*(M_PI/360.f);
CGFloat h = offsetY;
for (int x = 0; x <= waveWidth; x++) {
y = A*sin(ω*x + (- φ)) + h*1; //坐标系差异 需要转换 UIkit中 Y轴向下
}
其中
'ω中的1.1 是在总宽度waveWidth下显示1.1个波曲线的意思'
'φ中的360 决定了初相位,两个相同参数的水波浪,设置初相位不同即可有波动差异,达到水波浪交错效果'
ps:由于CoreGraphics坐标系基于os系统,所以和UIKit所在的iOS系统下坐标系是上下垂直的,所以Y值翻转一下,当然你不翻转,那么水波浪的波动方向就是不从左到右,而是从右到左.
效果
完整代码
WaveAnimationView.h
//
// WaveAnimationView.h
// iOSWaveAnimationDemo
//
// Created by tianNanYiHao on 2017/8/24.
// Copyright © 2017年 tianNanYiHao. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface WaveAnimationView : UIView
@end
WaveAnimationView.m
//
// WaveAnimationView.m
// iOSWaveAnimationDemo
//
// Created by tianNanYiHao on 2017/8/24.
// Copyright © 2017年 tianNanYiHao. All rights reserved.
//
#import "WaveAnimationView.h"
@interface WaveAnimationView (){
CAShapeLayer *waveLayer; //水波浪1
CAShapeLayer *waveLayerTwo; //水波浪2
CADisplayLink *displayLink; //垂直刷新link
CGFloat speed; //沿X轴波峰移动速度
CGFloat offsetX; //沿X轴波峰移动偏移量
CGFloat offsetY; //沿Y轴波起点位置(即公式中的h)
CGFloat waveWidth; //水波浪总宽度
CGFloat A; //上下的振幅
}
@end
@implementation WaveAnimationView
- (instancetype)initWithFrame:(CGRect)frame{
if ([super initWithFrame:frame]) {
speed = 2.f;
offsetX = 0.f;
offsetY = self.bounds.size.height *0.6f;
waveWidth = self.bounds.size.width;
A = 10.f;
//创建水波纹layer
waveLayer = [CAShapeLayer layer];
waveLayer.fillColor = [UIColor colorWithRed:3/255.f green:144/255.f blue:144/255.f alpha:1.f].CGColor;
[self.layer addSublayer:waveLayer];
//创建水波纹layer2
waveLayerTwo = [CAShapeLayer layer];
waveLayerTwo.fillColor = [UIColor colorWithRed:3/255.f green:144/255.f blue:144/255.f alpha:0.7f].CGColor;
[self.layer addSublayer:waveLayerTwo];
}return self;
}
- (void)drawRect:(CGRect)rect{
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawWaveLayer)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)drawWaveLayer{
/*
正弦型函数解析式:y=Asin(ωx+φ)+h
各常数值对函数图像的影响:
φ(初相位):决定波形与X轴位置关系或横向移动距离(左加右减)
ω:决定周期(最小正周期T=2π/|ω|)
A:决定峰值(即纵向拉伸压缩的倍数)
h:表示波形在Y轴的位置关系或纵向移动距离(上加下减)
*/
[self drawWaveLayerOne];
[self drawWaveLayerTwo];
}
- (void)drawWaveLayerOne{
//1.计算随着时间推移的X轴偏移量
offsetX += speed;
CGMutablePathRef path = CGPathCreateMutable();
//起点
CGPathMoveToPoint(path, nil, 0, offsetY);
CGFloat y;
CGFloat ω = 1.1*M_PI/waveWidth;
CGFloat φ = offsetX*(M_PI/360.f);
CGFloat h = offsetY;
for (int x = 0; x <= waveWidth; x++) {
y = A*sin(ω*x + (- φ)) + h*1; //坐标系差异 需要转换 UIkit中 Y轴向下
CGPathAddLineToPoint(path, nil, x, y);
}
//左下角点
CGPathAddLineToPoint(path, nil, waveWidth, self.bounds.size.height);
//右下角点
CGPathAddLineToPoint(path, nil, 0, self.bounds.size.height);
//闭合路径
CGPathCloseSubpath(path);
waveLayer.path = path;
//释放
CGPathRelease(path);
}
- (void)drawWaveLayerTwo{
//1.计算随着时间推移的X轴偏移量
offsetX += speed;
CGMutablePathRef path = CGPathCreateMutable();
//起点
CGPathMoveToPoint(path, nil, 0, offsetY);
CGFloat y;
CGFloat ω = 1.1*M_PI/waveWidth;
CGFloat φ = offsetX*(M_PI/270.f);
CGFloat h = offsetY;
for (int x = 0; x <= waveWidth; x++) {
y = A*sin(ω*x + (- φ)) + h*1; //坐标系差异 需要转换 UIkit中 Y轴向下
CGPathAddLineToPoint(path, nil, x, y);
}
//左下角点
CGPathAddLineToPoint(path, nil, waveWidth, self.bounds.size.height);
//右下角点
CGPathAddLineToPoint(path, nil, 0, self.bounds.size.height);
//闭合路径
CGPathCloseSubpath(path);
waveLayerTwo.path = path;
//释放
CGPathRelease(path);
}
@end