138 lines
4.3 KiB
Swift
138 lines
4.3 KiB
Swift
//
|
|
// AV_WaveView.swift
|
|
// anniversary_Project
|
|
//
|
|
// Created by 忆海16 on 2024/7/8.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
class AV_WaveView: UIView {
|
|
|
|
private var waveLayers: [CAShapeLayer] = []
|
|
private var waveHeight: CGFloat = 10.0
|
|
private var waveLength: CGFloat = 200.0
|
|
private var waveSpeed: CGFloat = 0.05
|
|
private var waveOffset: CGFloat = 0.0
|
|
private var wavePositionY: CGFloat = 0.0
|
|
private var totalCapacity: CGFloat = 0.0
|
|
private var currentVolume: CGFloat = 0.0
|
|
private var timer: Timer?
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
setupWaveLayers()
|
|
setupMaskLayer()
|
|
startWaveAnimation()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
super.init(coder: coder)
|
|
setupWaveLayers()
|
|
setupMaskLayer()
|
|
startWaveAnimation()
|
|
}
|
|
|
|
private func setupWaveLayers() {
|
|
let colors = [UIColor.hexSting(color: "#96D3FF", alpha: 1).cgColor,
|
|
UIColor.hexSting(color: "#62BFFF", alpha: 1).cgColor]
|
|
for color in colors {
|
|
let waveLayer = CAShapeLayer()
|
|
waveLayer.fillColor = color
|
|
layer.addSublayer(waveLayer)
|
|
waveLayers.append(waveLayer)
|
|
}
|
|
}
|
|
|
|
private func setupMaskLayer() {
|
|
let maskLayer = CAShapeLayer()
|
|
let path = UIBezierPath(rect: bounds)
|
|
maskLayer.path = path.cgPath
|
|
layer.mask = maskLayer
|
|
}
|
|
|
|
private func startWaveAnimation() {
|
|
timer = Timer.scheduledTimer(timeInterval: 1.0 / 30.0, target: self, selector: #selector(updateWave), userInfo: nil, repeats: true)
|
|
}
|
|
|
|
@objc private func updateWave() {
|
|
waveOffset += waveSpeed
|
|
for (index, waveLayer) in waveLayers.enumerated() {
|
|
let amplitude = waveHeight * (1.2 - 0.2 * CGFloat(index))
|
|
let path = createWavePath(offset: waveOffset + CGFloat(index) * 0.3, amplitude: amplitude)
|
|
waveLayer.path = path.cgPath
|
|
}
|
|
}
|
|
|
|
private func createWavePath(offset: CGFloat, amplitude: CGFloat) -> UIBezierPath {
|
|
let path = UIBezierPath()
|
|
let width = bounds.width
|
|
let height = bounds.height
|
|
let midY = height * wavePositionY
|
|
path.move(to: CGPoint(x: 0, y: midY))
|
|
|
|
for x in stride(from: 0.0, to: Double(width), by: 1.0) {
|
|
let relativeX = CGFloat(x) / waveLength * .pi * 2
|
|
let y = sin(relativeX + offset) * amplitude + midY
|
|
path.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.close()
|
|
|
|
return path
|
|
}
|
|
|
|
func setTotalCapacity(_ capacity: CGFloat) {
|
|
totalCapacity = capacity
|
|
currentVolume = 0
|
|
wavePositionY = 1.0
|
|
updateWavePosition(animated: false)
|
|
}
|
|
|
|
func drinkWater(_ volume: CGFloat) {
|
|
currentVolume += volume
|
|
if currentVolume > totalCapacity {
|
|
currentVolume = totalCapacity
|
|
}
|
|
|
|
let newPositionY = 1.0 - (currentVolume / totalCapacity)
|
|
updateWavePosition(animated: true, targetPositionY: newPositionY)
|
|
}
|
|
|
|
func setCurrentVolume(_ volume: CGFloat) {
|
|
currentVolume = volume
|
|
if currentVolume > totalCapacity {
|
|
currentVolume = totalCapacity
|
|
} else if currentVolume < 0 {
|
|
currentVolume = 0
|
|
}
|
|
|
|
let newPositionY = 1.0 - (currentVolume / totalCapacity)
|
|
updateWavePosition(animated: true, targetPositionY: newPositionY)
|
|
}
|
|
|
|
private func updateWavePosition(animated: Bool, targetPositionY: CGFloat? = nil) {
|
|
let newPositionY = targetPositionY ?? (1.0 - (currentVolume / totalCapacity))
|
|
|
|
if animated {
|
|
UIView.animate(withDuration: 3.0, delay: 0, options: [.curveEaseInOut], animations: {
|
|
self.wavePositionY = newPositionY
|
|
self.layer.setNeedsDisplay()
|
|
}, completion: { finished in
|
|
if finished {
|
|
print("Animation completed.")
|
|
}
|
|
})
|
|
} else {
|
|
wavePositionY = newPositionY
|
|
layer.setNeedsDisplay()
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
timer?.invalidate()
|
|
}
|
|
}
|