Created
February 25, 2023 23:27
-
-
Save kylehowells/9681243715760e06ae276ad50f40789c to your computer and use it in GitHub Desktop.
Animates a wave ripple along the top
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
// MARK: - WaveView | |
class WaveView: UIView { | |
private var shapeLayer: CAShapeLayer = CAShapeLayer() | |
// MARK: - Animation Properties | |
/// 0 - Static | |
/// 20 - Very fast | |
var speed: CGFloat = 10 | |
/// Wave Height | |
var frequency: CGFloat = 6.0 | |
/// Wave phase | |
private var phase: CGFloat = 0.0 | |
/// Wave color | |
var preferredColor: UIColor = UIColor.cyan { | |
didSet { | |
self.shapeLayer.fillColor = self.preferredColor.cgColor | |
self.shapeLayer.strokeColor = self.preferredColor.cgColor | |
} | |
} | |
// MARK: - Start Animation | |
enum Direction { | |
case right | |
case left | |
} | |
func animationStart(direction: Direction, speed: Double) { | |
self.preferredColor = UIColor.cyan | |
self.layer.addSublayer(self.shapeLayer) | |
if direction == .right { | |
self.speed = -speed | |
} else { | |
self.speed = speed | |
} | |
self.startDisplayLink() | |
} | |
// MARK: - Display Link | |
private weak var displayLink: CADisplayLink? | |
private var startTime: CFTimeInterval = 0 | |
private func startDisplayLink() { | |
self.startTime = CACurrentMediaTime() | |
self.displayLink?.invalidate() | |
let displayLink = CADisplayLink(target: self, selector:#selector(self.handleDisplayLink(_:))) | |
displayLink.add(to: .main, forMode: .common) | |
self.displayLink = displayLink | |
} | |
@objc private func handleDisplayLink(_ displayLink: CADisplayLink) { | |
self.phase = (CACurrentMediaTime() - self.startTime) * self.speed | |
self.updatePath() | |
} | |
private func stopDisplayLink() { | |
self.displayLink?.invalidate() | |
} | |
// MARK: - Layout | |
override func layoutSubviews() { | |
super.layoutSubviews() | |
self.shapeLayer.frame = self.bounds | |
self.updatePath() | |
} | |
func updatePath() { | |
let path: UIBezierPath = UIBezierPath() | |
let width: CGFloat = self.bounds.width | |
let height: CGFloat = self.bounds.height | |
let mid: CGFloat = height * 0.25 | |
let waveLength: CGFloat = width / self.frequency | |
let waveHeightCoef: CGFloat = self.frequency | |
path.move(to: CGPoint(x: 0, y: self.bounds.maxY)) | |
path.addLine(to: CGPoint(x: 0, y: mid)) | |
for x in stride(from: 0, through: width, by: 1) | |
{ | |
let actualX: CGFloat = x / waveLength | |
let sine: CGFloat = -sin((actualX + self.phase)) | |
let y: CGFloat = waveHeightCoef * sine + mid | |
path.addLine(to: CGPoint(x: x, y: y)) | |
} | |
path.addLine(to: CGPoint(x: CGFloat(width), y: self.bounds.maxY)) | |
path.addLine(to: CGPoint(x: 0, y: self.bounds.maxY)) | |
self.shapeLayer.path = path.cgPath | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment