动画

iOS水波动画实现

2016-12-15  本文已影响521人  JasonEVA

效果如下:


12345.gif

实现这个水波动画主要还是要利用下初中的知识:正弦函数

y =Asin(ωx+φ)+C 

A 表示振幅,也就是使用这个变量来调整波浪的高度 ω表示周期,也就是使用这个变量来调整在屏幕内显示的波浪的数量 φ表示波浪横向的偏移,也就是使用这个变量来调整波浪的流动 C表示波浪纵向的位置,也就是使用这个变量来调整波浪在屏幕中竖直的位置。

主要原理

CADisplayLink的对象也是一个定时器。适用于UI的不停刷新,如自定义动画引擎或者视频的渲染。CADisplayLink 对象注册到Runloop之后。屏幕刷新的时候定时器就会被触发。iOS设备的刷新频率是60HZ也就是60帧也就是每秒刷新60次,也可以通过设置frameInterval属性为2那么两帧才会刷新一次。 CAShapeLayer 的对象是一个本身没有形状,他的形状来源于你给定的Path,它依附于path,所以必须给定path,即使path不完整也会自动收尾相接,strokeStart以及stroleEnd代表着这个path中所占的百分比(可以使用storkeStart和stroleEnd来做曲线进度的动画)。

代码实现

创建一个动画View

#import <UIKit/UIKit.h>

@interface JWWavesAnimationView : UIView
- (void)setUp; //开始布局@end

.m

/*
 正弦函数
y =Asin(ωx+φ)+C
A 表示振幅,也就是使用这个变量来调整波浪的高度
ω表示周期,也就是使用这个变量来调整在屏幕内显示的波浪的数量
φ表示波浪横向的偏移,也就是使用这个变量来调整波浪的流动
C表示波浪纵向的位置,也就是使用这个变量来调整波浪在屏幕中竖直的位置。
*/


#import "JWWavesAnimationView.h"

@interface JWWavesAnimationView ()
@property (nonatomic, strong) CADisplayLink *waveDisplaylink;
@property (nonatomic, strong) CAShapeLayer *firstWaveLayer;
@property (nonatomic, strong) CAShapeLayer *secondWaveLayer;

@property (nonatomic) CGFloat waveA; //水纹振幅    表示上面的A
@property (nonatomic) CGFloat waveW;//水纹周期  表示上面的ω
@property (nonatomic) CGFloat offsetX; //位移   表示上面的φ
@property (nonatomic) CGFloat currentK; //当前波浪高度Y   表示上面的C
@property (nonatomic) CGFloat waveSpeed;//水纹速度   表示波浪流动的速度
@property (nonatomic) CGFloat waterWaveWidth; //水纹宽度
@property (nonatomic, strong) UIColor *firstWaveColor; //波浪的颜色

@end

@implementation JWWavesAnimationView

- (instancetype)init {
    if (self = [super init]) {
        self.backgroundColor = [UIColor whiteColor];
        self.layer.masksToBounds  = YES;
    }
    return self;
}

- (void)setUp {
    //设置波浪的宽度
    self.waterWaveWidth = self.bounds.size.width;
    //设置波浪的颜色
    self.firstWaveColor = [UIColor colorWithWhite:0.6 alpha:0.2];
    //设置波浪的速度
    self.waveSpeed = 0.4/M_PI;
    //初始化layer
    if (_firstWaveLayer == nil) {
        //初始化
        _firstWaveLayer = [CAShapeLayer layer];
        //设置闭环的颜色
        _firstWaveLayer.fillColor = _firstWaveColor.CGColor;
        //设置边缘线的颜色
//        _firstWaveLayer.strokeColor = [UIColor blueColor].CGColor;
//        //设置边缘线的宽度
//        _firstWaveLayer.lineWidth = 4.0;
//        _firstWaveLayer.strokeStart = 0.0;
//        _firstWaveLayer.strokeEnd = 0.8;
        [self.layer addSublayer:_firstWaveLayer];
    }
    if (self.secondWaveLayer == nil) {
        //初始化
        self.secondWaveLayer = [CAShapeLayer layer];
        //设置闭环的颜色
        self.secondWaveLayer.fillColor = _firstWaveColor.CGColor;
        //设置边缘线的颜色
        //        _firstWaveLayer.strokeColor = [UIColor blueColor].CGColor;
        //        //设置边缘线的宽度
        //        _firstWaveLayer.lineWidth = 4.0;
        //        _firstWaveLayer.strokeStart = 0.0;
        //        _firstWaveLayer.strokeEnd = 0.8;
        [self.layer addSublayer:self.secondWaveLayer];
    }
    //设置波浪流动速度
    self.waveSpeed = 0.1;
    //设置振幅
    self.waveA = 5;
    //设置周期
    self.waveW = 1/20.0;
    //设置波浪纵向位置
    self.currentK = self.frame.size.height/2;//屏幕居中
    //启动定时器
    self.waveDisplaylink = [CADisplayLink displayLinkWithTarget:self selector:@selector(getCurrentWave:)];
    [self.waveDisplaylink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

}

-(void)getCurrentWave:(CADisplayLink *)displayLink
{
    //实时的位移
    self.offsetX += self.waveSpeed;
    [self setCurrentFirstWaveLayerPath];
    [self setCurrentSecondWaveLayerPath];
}

-(void)setCurrentFirstWaveLayerPath
{
    //创建一个路径
    CGMutablePathRef path = CGPathCreateMutable();
    CGFloat y = self.currentK;
    //将点移动到 x=0,y=currentK的位置
    CGPathMoveToPoint(path, nil, 0, y);
    for (NSInteger x = 0.0f; x<=self.waterWaveWidth; x++) {
        //正玄波浪公式
        y = self.waveA * sin(self.waveW * x+ self.offsetX)+self.currentK;
        //将点连成线
        CGPathAddLineToPoint(path, nil, x, y);
    }
    CGPathAddLineToPoint(path, nil, self.waterWaveWidth, self.frame.size.height);
    CGPathAddLineToPoint(path, nil, 0, self.frame.size.height);
    CGPathCloseSubpath(path);
    _firstWaveLayer.path = path;
    CGPathRelease(path);
}

-(void)setCurrentSecondWaveLayerPath
{
    //创建一个路径
    CGMutablePathRef path = CGPathCreateMutable();
    CGFloat y = self.currentK;
    //将点移动到 x=0,y=currentK的位置
    CGPathMoveToPoint(path, nil, 0, y);
    for (NSInteger x = 0.0f; x<=self.waterWaveWidth; x++) {
        //正玄波浪公式
        y = (self.waveA+2) * sin(self.waveW * x- self.offsetX + 10)+self.currentK;
        //将点连成线
        CGPathAddLineToPoint(path, nil, x, y);
    }
    CGPathAddLineToPoint(path, nil, self.waterWaveWidth, self.frame.size.height);
    CGPathAddLineToPoint(path, nil, 0, self.frame.size.height);
    CGPathCloseSubpath(path);
    self.secondWaveLayer.path = path;
    CGPathRelease(path);
}

- (void)dealloc
{
    [_waveDisplaylink invalidate];
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

@end

使用

#import "JWWavesAnimationViewController.h"
#import "JWWavesAnimationView.h"
#import <Masonry/Masonry.h>
@interface JWWavesAnimationViewController ()

@end

@implementation JWWavesAnimationViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view setBackgroundColor:[UIColor lightGrayColor]];
    JWWavesAnimationView *waveView = [JWWavesAnimationView new];
    [waveView.layer setCornerRadius:50];
    [waveView setClipsToBounds:YES];

    [self.view addSubview:waveView];
    
    [waveView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
        make.width.height.equalTo(@100);
    }];
    
    [self.view layoutIfNeeded];
    [self.view setNeedsLayout];
    
    [waveView setUp];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end
上一篇下一篇

猜你喜欢

热点阅读