所謂波浪效果如圖:
看起來很柔和,很惹眼,如題目所說,作出這個效果需要用到
CAShapeLayer
和CADisplayLink
1纽乱、CAShapeLayer
CAShapeLayer
顧名思義芳绩,繼承于CALayer
掀亥。每個CAShapeLayer
對象都代表著將要被渲染到屏幕上的一個任意的形狀(shape
)。具體的形狀由其path
(類型為CGPathRef
)屬性指定妥色。 普通的CALayer
是矩形搪花,所以需要frame
屬性。 CAShapeLayer
初始化時也需要指定frame
值嘹害,但 它本身沒有形狀撮竿,它的形狀來源于其屬性path
.。(tip: 如果我們在使用時將CAShapeLayer
和UIBezierPath
相結(jié)合,就可以靈活的繪制出很多動畫效果了) 這個形狀不一定要閉合笔呀,圖層路徑也不一定要不可破幢踏,事實(shí)上你可以在一個圖層上繪制好幾個不同的形狀。你可以控制一些屬性比如lineWith
(線寬许师,用點(diǎn)表示單位)房蝉,lineCap
(線條結(jié)尾的樣子),和lineJoin
線條之間的結(jié)合點(diǎn)的樣子)微渠;但是在圖層層面你只有一次機(jī)會設(shè)置這些屬性搭幻。如果你想用不同顏色或風(fēng)格來繪制多個形狀,就不得不為每個形狀準(zhǔn)備一個圖層了逞盆。 CAShapeLayer
有不同于CALayer的屬性粗卜,它從CALayer
繼承而來的屬性在繪制時是不起作用的。相比直下纳击,使用CAShapeLayer
有以下一些優(yōu)點(diǎn):
- 渲染快速
CAShapeLayer
使用了硬件加速续扔,繪制同一圖形會比用Core Graphics
快很多。 - 高效使用內(nèi)存焕数。一個
CAShapeLayer
不需要像普通CALayer
一樣創(chuàng)建一個寄宿圖形纱昧,所以無論有多大,都不會占用太多的內(nèi)存堡赔。 - 不會被圖層邊界剪裁掉识脆。一個
CAShapeLayer
可以在邊界之外繪制。你的圖層路徑不會像在使用Core Graphics
的普通CALayer
一樣被剪裁掉善已。 - 不會出現(xiàn)像素化灼捂。當(dāng)你給
CAShapeLayer
做3D變換時,它不像一個有寄宿圖的普通圖層一樣變得像素化换团。
2悉稠、CADisplayLink
CADisplayLink
就像是一個定時器,每隔幾毫秒刷新一次屏幕艘包。能讓我們以和屏幕刷新頻率相同的頻率去刷新我們繪制到屏幕上的內(nèi)容的猛。CADisplayLink
使用方式如下:
timer = CADisplayLink(target: self, selector: #selector(waveEvent))
timer?.add(to: .current, forMode: .commonModes)
當(dāng)CADisplayLink
注冊到runloop
以后耀盗,屏幕刷新的時候就會調(diào)用綁定到它上面的target
所擁有的selector
方法。停止CADisplayLink
的運(yùn)行非常的簡單卦尊,只需要調(diào)用它的invalidate
方法叛拷。
說道定時器,我們肯定會想到NSTimer
岂却, CADisplayLink
的接口設(shè)計(jì)的和NSTimer
很類似忿薇,所以它實(shí)際上就是一個內(nèi)置實(shí)現(xiàn)的替代,但是和timeInterval
以秒為單位不同躏哩,CADisplayLink
有一個整型的frameInterval
屬性煌恢,指定了間隔多少幀之后才執(zhí)行。默認(rèn)值是1震庭,意味著每次屏幕更新之前都會執(zhí)行一次瑰抵。但是如果動畫的代碼執(zhí)行起來超過了六十分之一秒,你可以指定frameInterval
為2器联,就是說動畫每隔一幀執(zhí)行一次(一秒鐘30幀)二汛。正常情況下CADisplayLink
在屏幕每次刷新時都會調(diào)用,精確度非常高拨拓,并且CADisplayLink
的使用場合相對專一肴颊,適合做UI的不停重繪,比如動畫的連續(xù)繪制渣磷。CADisplayLink
會保證幀率足夠連續(xù)婿着,使得動畫看起來更加平滑。NSTimer
的使用范圍要廣泛很多醋界,可以做單次或者循環(huán)處理某個任務(wù)竟宋,精度相比CADisplayLink
要低。
3形纺、繪制波浪輪廓
繪制波浪輪廓丘侠,我們會想到三角形的正弦、余弦函數(shù)逐样,如圖是在單位為1的右手直角坐標(biāo)系中的曲線變化如下:
可以看到在(-2π , 2π )的范圍類蜗字,y值在[-1, 1]之間變化。
以正弦曲線為例脂新,它可以表示為y=Asin(ωx+φ)+k挪捕,公式中各符號表示的含義:
A–振幅,即波峰的高度争便。
(ωx+φ)–相位级零,反應(yīng)了變量y所處的位置。
φ–初相始花,x=0時的相位妄讯,反映在坐標(biāo)系上則為圖像的左右移動。
k–偏距酷宵,反映在坐標(biāo)系上則為圖像的上移或下移亥贸。
ω–角速度,控制正弦周期(單位角度內(nèi)震動的次數(shù))浇垦。
通過上面的函數(shù)炕置,我們就能計(jì)算出波浪曲線上任意位置的坐標(biāo)點(diǎn)。通過CGMutablePath
的函數(shù)addLineToPoint
來把這些點(diǎn)連接起來男韧,就構(gòu)建了波浪形狀的path
朴摊。只要我們的設(shè)定相鄰兩點(diǎn)的間距夠小,就能構(gòu)建出平滑的正弦曲線此虑,構(gòu)建正弦波浪的代碼如下:
for x in 0...Int(width) {
y = height * CGFloat(sinf(waveFrequency_f * Float(x) + offset_f))
path.addLine(to: CGPoint(x: CGFloat(x), y: y))
maskPath.addLine(to: CGPoint(x: CGFloat(x), y: -y))
}
在這里我們設(shè)定了兩個正弦曲線上的點(diǎn)的橫坐標(biāo)間距是1甚纲,現(xiàn)在來解釋一下通過橫坐標(biāo)x來得出y的計(jì)算過程:
y = height * CGFloat(sinf(waveFrequency_f * Float(x) + offset_f))
height
表示曲線的波峰值,(height
==> let height = CGFloat(priWaveHeight)
)朦前。waveFrequency_f
表示角速度介杆,也就是控件單位角度內(nèi)振動的次數(shù)。offset_f
代表偏距韭寸,由于我們需要讓波浪曲線的波峰在layer的范圍內(nèi)顯示春哨,所以需要將整個曲線向下移動波峰大小的單位,因?yàn)镃ALayer使用左手坐標(biāo)系恩伺,所以向下移動需要加上波峰的大小赴背。(offset_f
==> let offset_f = Float(offset * 0.045)
)
4、讓波浪曲線動起來
正弦或者余弦曲線上的點(diǎn)晶渠,不論角度如何凰荚,在y軸上的變化范圍在它的波峰與波谷之間。拿單位正交直角坐標(biāo)系來說褒脯,只要我們規(guī)律性的增加角度值浇揩,那么曲線上的點(diǎn)就會在[1, -1]之間變化。找到規(guī)律憨颠,我們就能讓波浪曲線動起來:
offset += priWaveSpeed
let width = frame.width
let height = CGFloat(priWaveHeight)
let path = CGMutablePath()
path.move(to: CGPoint(x: 0, y: height))
var y: CGFloat = 0
let maskPath = CGMutablePath()
maskPath.move(to: CGPoint(x: 0, y: height))
let offset_f = Float(offset * 0.045)
let waveFrequency_f = Float(0.01 * priFrequency)
for x in 0...Int(width) {
y = height * CGFloat(sinf(waveFrequency_f * Float(x) + offset_f))
path.addLine(to: CGPoint(x: CGFloat(x), y: y))
maskPath.addLine(to: CGPoint(x: CGFloat(x), y: -y))
}
path.addLine(to: CGPoint(x: width, y: height))
path.addLine(to: CGPoint(x: 0, y: height))
path.closeSubpath()
self.realWaveLayer.path = path
maskPath.addLine(to: CGPoint(x: width, y: height))
maskPath.addLine(to: CGPoint(x: 0, y: height))
maskPath.closeSubpath()
self.maskWaveLayer.path = maskPath
在這里胳徽, 使用CADisplayLink
來不斷刷新由CGMutablePath
創(chuàng)建的形狀,兩次刷新之間曲線的變化通過增加初相來實(shí)現(xiàn)爽彤, 初相:offset += priWaveSpeed
5养盗、最后
代碼在這兒??:https://github.com/irembeu/WaveView.git