项目需要,所以在网上找了一些资料做了Swift和OC两版。
动画所用到的函数:
正弦型函数解析式:y=Asin(ωx+φ)+h
各常数值对函数图像的影响:
φ(初相位):决定波形与X轴位置关系或横向移动距离
ω:决定周期(最小正周期T=2π/|ω|)
A:决定峰值
h:表示波形在Y轴的位置关系或纵向移动距离
效果图
OC版
#import
@interface SXMWaterWaveView : UIView
/** 波动速度 */
@property (nonatomic, assign) CGFloat waveSpeed;
/** 水波振幅 */
@property (nonatomic, assign) CGFloat waveAmplitude;
/** 水波颜色 */
@property (nonatomic, strong) UIColor *waveColor;
/** 水波的高度 */
@property (nonatomic, assign) CGFloat waveHeight;
- (void)destroy;
@end
#import "SXMWaterWaveView.h"
@interface SXMWaterWaveView ()
@property (nonatomic, strong) CAShapeLayer *firstShapeLayer;
@property (nonatomic, strong) CAShapeLayer *sencondShapeLayer;
@property (nonatomic, strong) CADisplayLink *waveDisplayLink;
@property (nonatomic, assign) CGFloat offsetX;
@property (nonatomic, assign) CGFloat waveWidth;
@end
@implementation SXMWaterWaveView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setupUI];
}
return self;
}
// 设置UI
- (void)setupUI
{
// 初始化值
self.waveSpeed = 0.1;
self.waveAmplitude = 8;
self.waveWidth = 2.5 * M_PI / self.bounds.size.width;
self.waveHeight = self.frame.size.height / 2;
self.firstShapeLayer = [CAShapeLayer layer];
self.firstShapeLayer.fillColor = [UIColor colorWithRed:255 / 255 green:255 / 255 blue:255 / 255 alpha:0.5].CGColor;
[self.layer addSublayer:self.firstShapeLayer];
self.sencondShapeLayer = [CAShapeLayer layer];
self.sencondShapeLayer.fillColor = [UIColor colorWithRed:255 / 255 green:255 / 255 blue:255 / 255 alpha:0.5].CGColor;
[self.layer addSublayer:self.sencondShapeLayer];
self.waveDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(getCurrentWave)];
[self.waveDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)getCurrentWave
{
self.offsetX += self.waveSpeed;
// 第一条线
CGMutablePathRef firstPath = CGPathCreateMutable();
CGPathMoveToPoint(firstPath, nil, 0, self.waveHeight);
CGFloat firstY = self.bounds.size.height / 2;
for (float x = 0.f; x <= self.bounds.size.width ; x++) {
firstY = self.waveAmplitude * sin(self.waveWidth * x + _offsetX) + self.waveHeight;
CGPathAddLineToPoint(firstPath, nil, x, firstY);
}
CGPathAddLineToPoint(firstPath, nil, self.bounds.size.width, self.frame.size.height);
CGPathAddLineToPoint(firstPath, nil, 0, self.frame.size.height);
// 结束绘图信息
CGPathCloseSubpath(firstPath);
self.firstShapeLayer.path = firstPath;
CGPathRelease(firstPath);
// 第二条线
CGMutablePathRef secondPath = CGPathCreateMutable();
CGPathMoveToPoint(secondPath, nil, 0, self.waveHeight+100);
CGFloat secondY = self.bounds.size.height / 2;
for (float x = 0.f; x <= self.bounds.size.width ; x++) {
secondY = self.waveAmplitude * sin(_waveWidth * x + _offsetX - self.bounds.size.width / 2) + self.waveHeight;
CGPathAddLineToPoint(secondPath, nil, x, secondY);
}
CGPathAddLineToPoint(secondPath, nil, self.bounds.size.width, self.frame.size.height);
CGPathAddLineToPoint(secondPath, nil, 0, self.frame.size.height);
CGPathCloseSubpath(secondPath);
self.sencondShapeLayer.path = secondPath;
CGPathRelease(secondPath);
}
- (void)setWaveColor:(UIColor *)waveColor
{
_waveColor = waveColor;
self.firstShapeLayer.fillColor = waveColor.CGColor;
self.sencondShapeLayer.fillColor = waveColor.CGColor;
}
- (void)destroy
{
[self.waveDisplayLink invalidate];
self.firstShapeLayer = nil;
self.sencondShapeLayer = nil;
self.waveDisplayLink = nil;
}
@end
Swift版
import UIKit
class SXMWaterWaveView_swift: UIView {
lazy private var firstShapeLayer = CAShapeLayer();
lazy private var sencondShapeLayer = CAShapeLayer();
lazy private var waveDisplayLink = CADisplayLink();
/** 波动速度 */
var waveSpeed : CGFloat = 0
/** 水波振幅 */
var waveAmplitude: CGFloat = 0
/** 水波的高度 */
var waveHeight : CGFloat = 0
/** 水波颜色 */
var waveColor : UIColor? {
didSet {
firstShapeLayer.fillColor = waveColor?.cgColor;
sencondShapeLayer.fillColor = waveColor?.cgColor;
}
}
private var waveWidth: CGFloat = 0
private var offsetX: CGFloat = 0
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
// 初始化值
self.waveSpeed = 0.1;
self.waveAmplitude = 8;
self.waveWidth = 2.5 * CGFloat(M_PI) / self.bounds.size.width;
self.waveHeight = self.frame.size.height / 2;
firstShapeLayer.fillColor = UIColor.init(colorLiteralRed: 255 / 255.0, green: 255 / 255.0, blue: 255 / 255.0, alpha: 0.5).cgColor
sencondShapeLayer.fillColor = UIColor.init(colorLiteralRed: 255 / 255.0, green: 255 / 255.0, blue: 255 / 255.0, alpha: 0.5).cgColor
layer.addSublayer(firstShapeLayer)
layer.addSublayer(sencondShapeLayer)
waveDisplayLink = CADisplayLink(target: self, selector: #selector(getCurrentWave))
waveDisplayLink.add(to: RunLoop.current, forMode: .commonModes)
}
@objc private func getCurrentWave() {
offsetX += waveSpeed
// 第一条线
let firstPath = CGMutablePath()
var firstY = bounds.size.width / 2
firstPath.move(to: CGPoint(x: 0, y: firstY))
for i in 0...Int(bounds.size.width) {
firstY = waveAmplitude * sin(waveWidth * CGFloat(i) + offsetX) + waveHeight
firstPath.addLine(to: CGPoint(x: CGFloat(i), y: firstY))
}
firstPath.addLine(to: CGPoint(x: bounds.size.width, y: bounds.size.height))
firstPath.addLine(to: CGPoint(x: 0, y: bounds.size.height))
firstPath.closeSubpath()
firstShapeLayer.path = firstPath
// 第二条线
let secondPath = CGMutablePath()
var secondY = bounds.size.width / 2
secondPath.move(to: CGPoint(x: 0, y: secondY))
for i in 0...Int(bounds.size.width) {
secondY = waveAmplitude * sin(waveWidth * CGFloat(i) + offsetX - bounds.size.width / 2 ) + waveHeight
secondPath.addLine(to: CGPoint(x: CGFloat(i), y: secondY))
}
secondPath.addLine(to: CGPoint(x: bounds.size.width, y: bounds.size.height))
secondPath.addLine(to: CGPoint(x: 0, y: bounds.size.height))
secondPath.closeSubpath()
sencondShapeLayer.path = secondPath
}
public func destroyView() {
waveDisplayLink.invalidate()
}
deinit {
print("SXMWaterWaveView_deinit")
}
}
Demo地址:https://github.com/LarkNan/SXMWaterWaveDemo
参考资料:http://www.jianshu.com/p/44c904291a2e
全文完