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.itdecent.cn/p/44c904291a2e

全文完

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 類似淘寶的效果: ?我們知道,計算機不可能繪制出一條完美的曲線,如果放大到像素的級別,可以看到這些曲線其實都是柵格...
    CholMay閱讀 1,381評論 0 7
  • 在網易新聞和其他的APP中,大家可能都觀察到了在個人中心里面,有一個水波紋一樣的動畫效果,這個功能還是非常的有用的...
    我在鄱陽湖邊閱讀 1,766評論 0 6
  • 深入理解傅里葉變換Mar 12, 2017 這原本是我在知乎上對傅立葉變換、拉普拉斯變換、Z變換的聯(lián)系?為什么要進...
    價值趨勢技術派閱讀 5,940評論 2 2
  • 說到阿嬌,可能我們90后還都可能會想到當年她和陳冠希當年的那個事件,95后的可能也會有知道。我經常會聽到有人說陳冠...
    ACE小飛閱讀 34,494評論 29 22
  • 今天看到朋友圈一段短文很好,轉發(fā)給大家。 在現(xiàn)實生活中,有些人脾氣粗暴,動不動就大發(fā)雷霆,有些人則慈眉善目,忍辱禮...
    cea0a3f05668閱讀 406評論 1 0

友情鏈接更多精彩內容