1. 程式人生 > >iOS水波動畫效果

iOS水波動畫效果

1:實現原理:兩條不同內填充色的波浪向相對方向平移,產生波動效果,使用的技術:CAShapeLayer,UIBezierPath

2:實現效果


3:實現步驟:

畫兩條貝塞爾曲線,由於需要交叉產生效果,當前螢幕和螢幕外相同寬度的部分畫線,然後按照固定的速度移動,每到移動到螢幕邊緣的時候重新設定波浪線的路徑,波浪的交叉重貼的部分產生的效果就是鎖需要的效果具體實現程式碼如下:

#import <UIKit/UIKit.h>

@interface WaveView : UIView

- (void)startWave;

@end

#import "WaveView.h"
#import "UIColor+Util.h"


@interface WPoint : NSObject

@property(nonatomic)CGFloat x;
@property(nonatomic)CGFloat y;

- (instancetype)initWithX:(CGFloat)x y:(CGFloat)y;

@end

@implementation WPoint

- (instancetype)initWithX:(CGFloat)x y:(CGFloat)y
{
    
    self = [super init];
    if (self) {
        
        _x = x;
        _y = y;
    }
    return self;

}
@end


@interface WaveView ()
{
    
    float mViewWidth,mLevelLine,mWaveHeight,mWaveWidth,mLeftSide,mRightSide,mMoveLen,mMoveLen1;
    int   mViewHeight;
    float speed;//水波平移速度
    float speedX;//每次平移在x軸的距離
    int   speedWave;//移動時間,time的呼叫時間
    
    NSMutableArray<WPoint *>  *mPointsList;
    NSMutableArray<WPoint *>  *mPointsList1;
    CAShapeLayer *mPaint;
    CAShapeLayer *mPaint1;
    UIBezierPath *mWavePath;
    UIBezierPath *mWavePath1;
    BOOL isMeasured;
    NSTimer *timer;
    
}
@end


@implementation WaveView

- (instancetype)initWithFrame:(CGRect)frame{

    self = [super initWithFrame:frame];
    if (self) {
        
         mPointsList = [NSMutableArray new];
         mPointsList1= [NSMutableArray new];
         mWaveHeight = 20;
         mWaveWidth  = 200;
         speed       = 0.8f;
         speedX      = 0.8f;
         speedWave   = 0.8;
         isMeasured = false;
        
         mPaint = [CAShapeLayer new];
         mPaint.fillColor = [UIColor colorWithHexString:@"#DFF2FF"].CGColor;
         mPaint.contentsScale         = [[UIScreen mainScreen] scale];
        
         mPaint1 = [CAShapeLayer new];
         mPaint1.fillColor = [UIColor colorWithHexString:@"#ffffff"].CGColor;
         mPaint1.contentsScale         = [[UIScreen mainScreen] scale];
        
         mWavePath = [UIBezierPath new];
         mWavePath1 = [UIBezierPath new];
         [self.layer addSublayer:mPaint];
         [self.layer addSublayer:mPaint1];
        
        
    }
    return self;

}


#pragma mark-----繪圖------
- (void)drawRect:(CGRect)rect{



    [mWavePath1 removeAllPoints];
    int j =  5;
    WPoint *point =  [mPointsList objectAtIndex:8];
    [mWavePath1 moveToPoint:CGPointMake(point.x, point.y)];
    
    for (; j >= -1; j = j - 2){
        
        WPoint *point1 =  [mPointsList objectAtIndex:j + 1];
        WPoint *point2  =  [mPointsList objectAtIndex:j + 2];
        
        [mWavePath1 addQuadCurveToPoint:CGPointMake(point1.x, point1.y) controlPoint:CGPointMake(point2.x, point2.y)];
    }
    
    WPoint *point3 =  [mPointsList objectAtIndex:0];
    
    [mWavePath1 addLineToPoint:CGPointMake(point3.x, mViewHeight)];
    [mWavePath1 addLineToPoint:CGPointMake(mRightSide, mViewHeight)];
    [mWavePath1 closePath];
    mPaint1.path = mWavePath1.CGPath;
    
    
    [mWavePath removeAllPoints];
    int i = 0;
    
    WPoint *point4 =  [mPointsList1 objectAtIndex:0];
    [mWavePath moveToPoint:CGPointMake(point4.x, point4.y)] ;
    
    for (; i < mPointsList1.count - 2; i = i + 2) {
        
        WPoint *point5 =  [mPointsList1 objectAtIndex:i + 2];
        WPoint *point6  =  [mPointsList1 objectAtIndex:i + 1];
        
        
        [mWavePath addQuadCurveToPoint:CGPointMake(point5.x, point5.y) controlPoint:CGPointMake(point6.x, point6.y)];
    }
    WPoint *point7 =  [mPointsList1 objectAtIndex:8];
    
    [mWavePath addLineToPoint:CGPointMake(point7.x, mViewHeight)];
    [mWavePath addLineToPoint:CGPointMake(mLeftSide, mViewHeight)];
    [mWavePath closePath];
    //mPaint的Style是FILL,會填充整個Path區域
    mPaint.path = mWavePath.CGPath;
   
}


#pragma mark---開始和結束動畫------

- (void)startWave{

   
    if (!timer) {
        
        [timer invalidate];
        timer = nil;

    }
    
    timer = [NSTimer timerWithTimeInterval:speedWave target:self selector:@selector(updateHandler) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
    
}

- (void)layoutSubviews
{
    if (!isMeasured) {
        
        CGSize size = self.frame.size;
        isMeasured = true;
        mViewHeight = size.height;
        mViewWidth = size.width/4.0f;
        
        mLevelLine = mViewHeight / 2;
        // 根據View寬度計算波形峰值
        mWaveHeight = mViewWidth / 10.0f;
        // 波長等於四倍View寬度也就是View中只能看到四分之一個波形,這樣可以使起伏更明顯
        mWaveWidth = mViewWidth * 4;
        // 左邊隱藏的距離預留一個波形
        mLeftSide = -mWaveWidth;
        mRightSide = mWaveWidth*2;
        // 這裡計算在可見的View寬度中能容納幾個波形,注意n上取整
        
        int n = (int) ceil(mViewWidth / mWaveWidth + 0.5);
        // n個波形需要4n+1個點,但是我們要預留一個波形在左邊隱藏區域,所以需要4n+5個點
        for (int i = 0; i < (4 * n + 5); i++){
            // 從P0開始初始化到P4n+4,總共4n+5個點
            float x = i * mWaveWidth / 4 - mWaveWidth;
            float y = 0;
            switch (i % 4){
                case 0:
                case 2:
                    // 零點位中線上
                    y = mLevelLine;
                    break;
                case 1:
                    // 往下波動的控制點
                    y = mLevelLine + mWaveHeight;
                    break;
                case 3:
                    // 往上波動的控制點
                    y = mLevelLine - mWaveHeight;
                    break;
            }
            
            [mPointsList  addObject:[[WPoint alloc]initWithX:x y:y]];
            [mPointsList1 addObject:[[WPoint alloc]initWithX:x y:y]];
        }
        //----將第一條波紋翻轉----點上下顛倒-----
        int h=1;
        for (int t=1;t<mPointsList1.count;t=t+2){
            
            WPoint *point = [mPointsList1 objectAtIndex:t];
            
            if (h%2==1){
                
                
                point.y  = point.y  - mWaveHeight*2;
                
            }else {
                
                point.y  = point.y  + mWaveHeight*2;
                
            }
            h++;
        }
        
        //---第二條波紋移動到螢幕右邊------
        
        for (int x=0;x<mPointsList.count;x++){
            
            WPoint *point = [mPointsList objectAtIndex:x];
            
            point.x =point.x + mWaveWidth;
        }
    }



}


- (void)stopWaveLine{

    if (!timer) {
        
        [timer invalidate];
        timer = nil;
        
    }
    
}



- (void)updateHandler{


    // 記錄平移總位移
    mMoveLen  += speed;
    mMoveLen1 += speedX;
    
    if (mLevelLine < mViewHeight/2)
        mLevelLine = mViewHeight/2;
    mLeftSide += speed;
    mRightSide -= speedX;
    // 波形平移
    for (int i = 0; i < mPointsList.count; i++){
        
        WPoint *point = [mPointsList objectAtIndex:i];
        point.x -= speed;
        
    }
    for (int i = 0; i < mPointsList1.count; i++){
        
        WPoint *point = [mPointsList1 objectAtIndex:i];
        point.x += speedX;
    }
    if (mMoveLen >= mWaveWidth){
        // 波形平移超過一個完整波形後復位
        mMoveLen = 0;
        [self resetPoints];
    }
    if (mMoveLen1 >= mWaveWidth) {
        // 波形平移超過一個完整波形後復位
        mMoveLen1 = 0;
        [self resetPoints1];
    }
    
    [self setNeedsDisplay];
    
}

#pragma mark--------重置關鍵點----

- (void)resetPoints{

    mLeftSide = -mWaveWidth;
    for (int i = 0; i < mPointsList1.count; i++){
        
        WPoint *pointValue    = [mPointsList1 objectAtIndex:i];
                pointValue.x  = i * mWaveWidth / 4 - mWaveWidth;
        
    }
}

- (void)resetPoints1{
    
    
    
    mRightSide = mWaveWidth*2;
    for (int i = 0; i < mPointsList.count; i++){
        
         WPoint *pointValue    = [mPointsList objectAtIndex:i];
                 pointValue.x  = i * mWaveWidth / 4;
    }
    
}

@end

3:使用:

    WaveView *waveView = [[WaveView alloc]initWithFrame:self.view.bounds];
    [waveView setBackgroundColor:[UIColor colorWithHexString:@"#48DEBF"]];
    [self.view addSubview:waveView];
    [waveView startWave];