水波纹效果
网上虽然很多水波纹动画效果实现的文章,但是我觉得有点讲解的不清楚,有点过于深化了。这篇我喜欢用笨方法让大家理解波纹曲线实现原理。
知识点
主要用到数学公式:正弦型函数解析式:y = A * sin(ωx+φ) + h
A:决定峰值(即纵向拉伸压缩的倍数),通俗点叫振幅。
h:表示波形在Y轴的位置关系或纵向移动距离(上加下减)。
φ:初相位:决定波形与X轴位置关系或横向移动距离(左加右减),和波纹运动速度有关系。
ω:决定周期(最小正周期T=2π/|ω|),在实际中一般一个周期就是view的width,可以求出ω = 2 * π/view.bounds.width
x:就是每一个点x的坐标,你所看到的水波纹曲线实际是由系统一个一个点连接而成的。
分析
1.如果你想理解绘制原理,在开始学习的时候可以把A/h/ω
这几个值写成固定值,φ这个值和速度有关系,你可以给定一个初始速度从而得到每个时间段的相位值如:offsetX += waveSpeed。
2.先绘制单个波浪曲线有利益你分析实现原理从而实现二段或者其他样式的水波纹。
3.绘制类准备:CAShaperLayer和CADisplayLink(因为帧率每秒60FPS)。
4.去调节改变公式里面各个参数的值看波纹曲线变化,写出适合你项目的波纹曲线。
主要代码
/**
处理displaylink事件,比较要注意的是当循环结束添加line
wavePath1.addLine(to: CGPoint.init(x: bounds.width, y: bounds.height))
wavePath1.addLine(to: CGPoint.init(x: 0, y: bounds.height))
因为我的坐标系是自上而下的,因此注意最后两个点绘制坐标。
*/
@objc private func handleTimer()
{
let wavePath1 = UIBezierPath()
let wavePath2 = UIBezierPath()
//振幅系数,振幅越大波峰越陡
waveAmplitude = bounds.height * rate * 0.1
//正弦型函数解析式:y = A * sin(ωx+φ) + h ω 读alpha
var y1 = (1 - rate) * bounds.height
var y2 = y1
wavePath1.move(to: CGPoint.init(x: 0, y: y1))
wavePath2.move(to: CGPoint.init(x: 0, y: y2))
//波长里面有几个波曲线可设置
let a = waveCycle
//当offsetX趋近CGFloat maxValue值时这个值应减减防止奔溃
if offsetX >= CGFloat.greatestFiniteMagnitude {
offsetX = offsetX - waveSpeed
}else {
offsetX = offsetX + waveSpeed
}
//峰值
let h = (1 - rate) * bounds.height
for x in 0...Int(bounds.width) {
//控制波浪1和波浪2的速度不同形成动画效果
let f1 = offsetX / bounds.width * .pi
let f2 = 0.75 * offsetX / bounds.width * .pi
y1 = waveAmplitude * CGFloat(sin(Double(a * CGFloat(x) + f1))) + (h - 10)
y2 = waveAmplitude * CGFloat(sin(Double(a * CGFloat(x) + f2))) + h
wavePath1.addLine(to: CGPoint.init(x: CGFloat(x), y: y1))
wavePath2.addLine(to: CGPoint.init(x: CGFloat(x), y: y2))
}
wavePath1.addLine(to: CGPoint.init(x: bounds.width, y: bounds.height))
wavePath1.addLine(to: CGPoint.init(x: 0, y: bounds.height))
wavePath1.close()
waveLayer1.path = wavePath1.cgPath
wavePath2.addLine(to: CGPoint.init(x: bounds.width, y: bounds.height))
wavePath2.addLine(to: CGPoint.init(x: 0, y: bounds.height))
wavePath2.close()
waveLayer2.path = wavePath2.cgPath
}
总结
1.主要是要理解正弦函数:y = A * sin(ωx+φ) + h 这几个参数对波纹的影响,你基本都能知道原理了,封装各种样式的水波纹效果离你不远了。
2.同时也要注意我手机的坐标是自上而下,从左往右的,(0,0)、(width,0)、(height,0)和(width,height),在连接最后两个点的时候需要注意下。