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];


1、波纹动画主要依赖于CAShapeLayer的绘制,使用帧动画实现;需要使用多个CAShapeLayer通过y值变换组成(这里我只是用了2个CAShapeLayer)。 2、渐变色由CAGradientLayer完成。 ``` - (void)changeFirstWaveLayerPath { CGMutablePathRef path = CGPathCreateMutable(); CGFloat y = _wavePointY; CGPathMoveToPoint(path, nil, 0, y); for (float x = 0.0f; x <= _waveWidth; x ) { y = _waveAmplitude * 1.6 * sin((250 / _waveWidth) * (x * M_PI / 180) - _waveOffsetX * M_PI / 270) _wavePointY; CGPathAddLineToPoint(path, nil, x, y); } CGPathAddLineToPoint(path, nil, _waveWidth, 0); CGPathAddLineToPoint(path, nil, 0, 0); CGPathCloseSubpath(path); _shapeLayer1.path = path; CGPathRelease(path); } - (void)changeSecondWaveLayerPath { CGMutablePathRef path = CGPathCreateMutable(); CGFloat y = _wavePointY; CGPathMoveToPoint(path, nil, 0, y); for (float x = 0.0f; x <= _waveWidth; x ) { y = _waveAmplitude * 1.6 * sin((250 / _waveWidth) * (x * M_PI / 180) - _waveOffsetX * M_PI / 180) _wavePointY; CGPathAddLineToPoint(path, nil, x, y); } CGPathAddLineToPoint(path, nil, _waveWidth, 0); CGPathAddLineToPoint(path, nil, 0, 0); CGPathCloseSubpath(path); _shapeLayer2.path = path; CGPathRelease(path); } ``` 方法调用: ``` _waveOffsetX = _waveSpeed; [self changeFirstWaveLayerPath]; [self changeSecondWaveLayerPath]; [self.layer addSublayer:self.gradientLayer1]; self.gradientLayer1.mask = _shapeLayer1; [self.layer addSublayer:self.gradientLayer2]; self.gradientLayer2.mask = _shapeLayer2; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值