【iOS】儲水罐波紋樣式button 以及 隨機路線的泡泡

話說又有這麼一個需求……產品想讓項目中增加一個按鈕:一個點擊了之後會有波紋上升的按鈕。在波紋到達頂端的時候還會有三個泡泡出現,以隨機的路線不同的速度漂浮到固定的一個點……
好吧,你贏了。
效果

先來完成這個波紋按鈕


/*
 正弦函數
 y =Asin(ωx+φ)+C
 A 表示振幅,也就是使用這個變量來調整波浪的高度
 ω表示週期,也就是使用這個變量來調整在屏幕內顯示的波浪的數量
 φ表示波浪橫向的偏移,也就是使用這個變量來調整波浪的流動
 C表示波浪縱向的位置,也就是使用這個變量來調整波浪在屏幕中豎直的位置。
 */
 
 {
    UIImage *_img;
    BOOL _once;
}

@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIImageView *imageView;
@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; //波浪的顏色

屬性大致就是這些。當按鈕被點擊的時候顯示波浪

//按鈕被點擊
-(void)viewDidClicked
{
    //動畫效果下只能點擊一次
    if (_once) {
        return;
    }
    
    if (self.animation) {
        //創建波浪
        _once = YES;
        [self createWave];
    }else{
        //回調
        if (self.buttonDidClick) {
            self.buttonDidClick();
        }
        if ([self.delegate respondsToSelector:@selector(waveButtonDidClicked)]) {
            [self.delegate waveButtonDidClicked];
        }
    }
}

#pragma mark - 創建波浪
-(void)createWave{
    //設置波浪的寬度
    self.waterWaveWidth = self.bounds.size.width;
    //設置波浪的顏色
    self.firstWaveColor = RGBA(250, 188, 16, 1);
    //設置波浪的速度
    self.waveSpeed = 0.4/M_PI;
    
    //初始化layer
    if (_firstWaveLayer == nil) {
        //初始化
        _firstWaveLayer = [CAShapeLayer layer];
        //設置閉環的顏色
        _firstWaveLayer.fillColor = _firstWaveColor.CGColor;
        [self.layer addSublayer:_firstWaveLayer];
    }
    //這裏設置兩個layer疊加起來豐富層次
    if (self.secondWaveLayer == nil) {
        //初始化
        self.secondWaveLayer = [CAShapeLayer layer];
        //設置閉環的顏色
        self.secondWaveLayer.fillColor = _firstWaveColor.CGColor;
        [self.layer addSublayer:self.secondWaveLayer];
    }
    
    [self bringSubviewToFront:self.titleLabel];
    [self bringSubviewToFront:self.imageView];
    
    //設置波浪流動速度
    self.waveSpeed = 0.5;
    //設置振幅
    self.waveA = 2;
    //設置週期
    self.waveW = 1/20.0;
    //設置波浪縱向位置
    self.currentK = self.frame.size.height;//屏幕居中
    //啓動定時器
    self.waveDisplaylink = [CADisplayLink displayLinkWithTarget:self selector:@selector(getCurrentWave:)];
    [self.waveDisplaylink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

//定時器調用的方法
-(void)getCurrentWave:(CADisplayLink *)displayLink
{
    //實時的位移
    self.offsetX += self.waveSpeed;
    if (self.currentK > 0) {
        self.currentK --;
        //使波浪動起來
        [self setCurrentFirstWaveLayerPath];
        [self setCurrentSecondWaveLayerPath];
    } else {
    	//波浪到達了頂點
        self.backgroundColor = self.firstWaveColor;
        
        //移除兩個波浪層
        [self.firstWaveLayer removeFromSuperlayer];
        [self.secondWaveLayer removeFromSuperlayer];
        
        //銷燬計時器
        [_waveDisplaylink invalidate];
        
        //回調
        if (self.buttonDidClick) {
            self.buttonDidClick();
        }
        if ([self.delegate respondsToSelector:@selector(waveButtonDidClicked)]) {
            [self.delegate waveButtonDidClicked];
        }
    }
}

//創建波浪路徑,其實這兩個方法也可以寫成一個通過參數調整的方法,不過當時懶……
-(void)setCurrentFirstWaveLayerPath
{
    //創建一個路徑
    UIBezierPath *path = [UIBezierPath new];
    CGFloat y = self.currentK;
    //將點移動到 x=0,y=currentK的位置
    [path moveToPoint:CGPointMake(0, y)];
    for (NSInteger x = 0.0f; x<=self.waterWaveWidth; x++) {
        //正玄波浪公式
        y = self.waveA * sin(self.waveW * x + self.offsetX)+self.currentK;
        //將點連成線
        [path addLineToPoint:CGPointMake(x, y)];
    }
    [path addLineToPoint:CGPointMake(self.waterWaveWidth, self.frame.size.height)];
    [path addLineToPoint:CGPointMake(0, self.frame.size.height)];
    self.firstWaveLayer.path = path.CGPath;
    
}

-(void)setCurrentSecondWaveLayerPath
{
    //創建一個路徑
    UIBezierPath *path = [UIBezierPath new];
    CGFloat y = self.currentK;
    //將點移動到 x=0,y=currentK的位置
    [path moveToPoint:CGPointMake(0, y)];
    for (NSInteger x = 0.0f; x<=self.waterWaveWidth; x++) {
        //正玄波浪公式
        y = (self.waveA+2) * sin((self.waveW) * x - self.offsetX + 10)+self.currentK;
        //將點連成線
        [path addLineToPoint:CGPointMake(x, y)];
    }
    [path addLineToPoint:CGPointMake(self.waterWaveWidth, self.frame.size.height)];
    [path addLineToPoint:CGPointMake(0, self.frame.size.height)];
    self.secondWaveLayer.path = path.CGPath;
}

這樣帶有波浪的按鈕就完成了。繼續做按鈕完成時出現的泡泡。

//泡泡視圖的初始化方法,參數是泡泡的起始點和終點
-(instancetype)initWithFrame:(CGRect)frame startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint{
    if (self = [super initWithFrame:frame]) {
        self.backgroundColor = [UIColor clearColor];
        self.startPoint = startPoint;
        self.endPoint = endPoint;
        //泡泡是默認隱藏的
        [self addSubview:self.bubble1];
        [self addSubview:self.bubble2];
        [self addSubview:self.bubble3];
        [self initPath];
        _bubbleArray = @[self.bubble1, self.bubble2, self.bubble3];
    }
    return self;
}

//創建路徑
-(void)initPath{
    for (int i=1; i<4; i++) {
        //第一條路徑
        UIBezierPath *path = [UIBezierPath bezierPath];
        //移動到起點
        [path moveToPoint:self.startPoint];
        //隨機一個控制點,x最大是屏幕寬-小球的寬度,這樣才能顯示小球
        CGFloat x1 = arc4random_uniform(ScreenWidth/2)+ScreenWidth/2-24;
        //轉折點在起點終點中間位置,上下波動不超過30
        CGFloat y1 = arc4random_uniform(30)+(self.startPoint.y-self.endPoint.y)/2;
        //添加控制點
        [path addQuadCurveToPoint:self.endPoint controlPoint:CGPointMake(x1, y1)];
        //設置曲線類型
        path.lineJoinStyle = kCGLineJoinRound;

        if (i==1) {
            self.path1 = path;
        }else if (i==2){
            self.path2 = path;
        }else{
            self.path3 = path;
        }
    }
    _pathArray = @[self.path1, self.path2, self.path3];
}

這樣泡泡和路徑都設置好了,只要在波紋按鈕升到定點的回調方法中調用泡泡視圖的show。

-(void)show{
    for (int i=0; i<_bubbleArray.count; i++) {
        UIImageView *bubble = _bubbleArray[i];
        CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        //設置路徑
        animation.path = [_pathArray[i] CGPath];
        //設置重複次數
        animation.repeatCount = 1;
        //動畫時長
        animation.duration = 1+i*0.25;
        animation.delegate = self;
        //完成後不移除
        animation.removedOnCompletion = NO;
        //
        animation.fillMode = kCAFillModeForwards;
        [animation setValue:@(i) forKey:@"count"];
        
        //添加動畫
        bubble.alpha = 1;
        [bubble.layer addAnimation:animation forKey:@""];
    }
}

最後在動畫結束的協議方法中將泡泡視圖隱藏掉:

-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    if (flag) {
        NSInteger count = [[anim valueForKey:@"count"] integerValue];
        UIImageView *bubble = _bubbleArray[count];
        bubble.alpha = 0;
        if (count == 2) {
            //回調
            if (self.animationDidEnd) {
                self.animationDidEnd();
            }
        }
    }
}

就完成啦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章