天天看點

iOS CAShapeLayer、CADisplayLink 實作波浪動畫效果

效果圖

代碼已上傳 GitHub:htt0p s : / / g i t hu b . c o m/ S il e n ce - G i t H u b /C o r e A n im a t i o n D em o 

可以自定義波浪高度、寬度、速度、方向、漸變速度、水的深度等參數。

實作原理

波浪的形狀繪制在 CAShapeLayer 上。通過 CADisplayLink 與螢幕重新整理頻率同步,每次重新整理都繪制新的波浪,并改變小船的位置和角度。另外,水和天空的顔色是漸變的,由 CAGradientLayer 實作,其中,顯示水的 CAGradientLayer 需要有波浪形狀的 CAShapeLayer 的遮罩(mask)。

CAShapeLayer

CAShapeLayer 的屬性 path (CGPath)就是圖層要顯示的形狀。把波浪的形狀繪制出來,指派給此屬性即可。

CADisplayLink

建立 CADisplayLink,相應的 target 實作螢幕重新整理時要調用的方法。把 CADisplayLink 加入 RunLoop 中。通過 isPaused 屬性控制 CADisplayLink 是否暫停(target 是否調用方法)

private var waveLink: CADisplayLink?      
waveLink = CADisplayLink(target: self, selector: #selector(waveLinkRefresh))
waveLink?.isPaused = truewaveLink?.add(to: .current, forMode: .defaultRunLoopMode)      

繪制波浪

波浪的形狀關鍵是正弦函數曲線

y = A * sin(x + B)      

參數 A 決定了波浪的高度;參數 B 決定了波浪在 x 軸的位置。

用一個屬性 currentPhase 表示參數 B

private var currentPhase: CGFloat = 0      

每次螢幕重新整理的時候用 currentPhase 繪制,然後更新此屬性,加上一個固定的數。這樣波浪就會朝左或右勻速移動。

為了使波浪高度逐漸變化,用一個屬性表示參數 A,然後每次繪制後更新此屬性,加上一個固定的數,直到波浪高度達到目标值。

小船的位置和旋轉角度

已知小船 x 軸坐标,通過正弦函數可以直接計算出小船的 y 軸坐标。此外,小船需要随着波浪旋轉,旋轉至船底與波浪表面相切。這就要對正弦函數進行求導

y' = A * cos(x + B)      

用以上式子計算出小船所在位置的 y',表示正弦函數在此處的切線斜率,幾何意義是切線與 x 軸的夾角的正切值。反正切即可求出切線與 x 軸的夾角,也就是小船需要旋轉的角度

angle = atan(y')      

用以上旋轉角度,改變小船視圖(UIView)的 transform,調用 CGAffineTransform 的 rotated(by:) 方法,實作小船的旋轉。

CAGradientLayer

CAGradientLayer 預設的顔色漸變方向是由上至下。給 colors 屬性指派一個包含 CGColor 的數組,則圖層顔色由上至下,從數組第一個值經中間值漸變至最後一個值。

顯示水的 CAGradientLayer 需要呈現波浪形狀,需要 CAShapeLayer 的遮罩。把繪制好波浪形狀的 CAShapeLayer 指派給 CAGradientLayer 的 mask 屬性即可。

繼續閱讀