Swift、OC版水波动画

项目需要,所以在网上找了一些资料做了Swift和OC两版。

动画所用到的函数:
正弦型函数解析式:y=Asin(ωx+φ)+h
各常数值对函数图像的影响:
φ(初相位):决定波形与X轴位置关系或横向移动距离
ω:决定周期(最小正周期T=2π/|ω|)
A:决定峰值
h:表示波形在Y轴的位置关系或纵向移动距离

<h4>效果图</h4>

waterwave.gif

<h4>OC版</h4>

#import <UIKit/UIKit.h>

@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

<h4>Swift版</h4>

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

全文完

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 类似淘宝的效果:  我们知道,计算机不可能绘制出一条完美的曲线,如果放大到像素的级别,可以看到这些曲线其实都是栅格...
    CholMay阅读 4,966评论 0 7
  • 在网易新闻和其他的APP中,大家可能都观察到了在个人中心里面,有一个水波纹一样的动画效果,这个功能还是非常的有用的...
    我在鄱阳湖边阅读 5,604评论 0 6
  • 深入理解傅里叶变换Mar 12, 2017 这原本是我在知乎上对傅立叶变换、拉普拉斯变换、Z变换的联系?为什么要进...
    价值趋势技术派阅读 11,068评论 2 2
  • 说到阿娇,可能我们90后还都可能会想到当年她和陈冠希当年的那个事件,95后的可能也会有知道。我经常会听到有人说陈冠...
    ACE小飞阅读 34,411评论 29 22
  • 今天看到朋友圈一段短文很好,转发给大家。 在现实生活中,有些人脾气粗暴,动不动就大发雷霆,有些人则慈眉善目,忍辱礼...
    cea0a3f05668阅读 2,875评论 1 0