iOS開發 - 用UIBezierPath實現果凍效果

#效果

elasticDemo.gif

DEMO


#邏輯

  • 下圖p1,藍色部分圖形是一個CAShapeLayer,他的形狀由UIBezierPath的路徑組成的。git

  • 這個路徑是由r1,r2,r3,r4,r5這5個紅點肯定的。其中r1,r2,r3,r4都是不動點,惟一能夠動的是r5點github

  • 根據上面的動態圖能夠看出,CAShapeLayer的形狀是隨着r5紅點的移動而相應變化的,因此只要得到r5的座標變化就能夠用UIBezierPath作出相應的路徑,而後就能夠造成相應的形狀。oop

p1.jpg

#實現

  • 初始化CAShapeLayer
- (void)configShapeLayer
{ 
    _shapeLayer = [CAShapeLayer layer];
    _shapeLayer.fillColor = [UIColor colorWithRed:57/255.0 green:67/255.0 blue:89/255.0 alpha:1.0].CGColor;
    [self.layer addSublayer:_shapeLayer];
}
  • 初始化r5點
- (void)configCurveView
{
    // _curveView就是r5點
    _curveX = SYS_DEVICE_WIDTH/2.0;       // r5點x座標
    _curveY = MIN_HEIGHT;                 // r5點y座標
    _curveView = [[UIView alloc] initWithFrame:CGRectMake(_curveX, _curveY, 3, 3)];
    _curveView.backgroundColor = [UIColor redColor];
    [self addSubview:_curveView];
}
  • 添加移動手勢&CADisplayLink
- (void)configAction
{
    _mHeight = 100;                       // 手勢移動時相對高度
    _isAnimating = NO;                    // 是否處於動效狀態

    // 手勢
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanAction:)];
    self.userInteractionEnabled = YES;
    [self addGestureRecognizer:pan];
    
    // calculatePath方法是算出在運行期間_curveView的座標,從而肯定_shapeLayer的形狀
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(calculatePath)];
    [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    // 在手勢結束的時候才調用calculatePath方法,因此一開始是暫停的
    _displayLink.paused = YES;    
}
  • 手勢解析
  • 手勢移動時,r5紅點跟着手勢移動,_shapeLayer則根據r5的座標來擴大本身的區域
  • 手勢結束時,r5紅點經過UIView的動畫方法來改變r5的座標,同時_shapeLayer根據r5的座標縮小本身的區域並最終返回原形。
- (void)handlePanAction:(UIPanGestureRecognizer *)pan
{
    if(!_isAnimating)
    {
        if(pan.state == UIGestureRecognizerStateChanged)
        {
            // 手勢移動時,_shapeLayer跟着手勢向下擴大區域
            CGPoint point = [pan translationInView:self];
            
            // 這部分代碼使r5紅點跟着手勢走
            _mHeight = point.y*0.7 + MIN_HEIGHT;
            _curveX = SYS_DEVICE_WIDTH/2.0 + point.x;
            _curveY = _mHeight > MIN_HEIGHT ? _mHeight : MIN_HEIGHT;
            _curveView.frame = CGRectMake(_curveX,
                                          _curveY,
                                          _curveView.frame.size.width,
                                          _curveView.frame.size.height);
            
            // 根據_curveView的位置,更新_shapeLayer形狀
            [self updateShapeLayerPath];
            
        }
        else if (pan.state == UIGestureRecognizerStateCancelled ||
                 pan.state == UIGestureRecognizerStateEnded ||
                 pan.state == UIGestureRecognizerStateFailed)
        {
            // 手勢結束時,_shapeLayer返回原狀併產生彈簧動效
            _isAnimating = YES;
            _displayLink.paused = NO;           //開啓displaylink,會執行方法calculatePath.
            
            // 彈簧動效
            [UIView animateWithDuration:1.0
                                  delay:0.0
                 usingSpringWithDamping:0.5
                  initialSpringVelocity:0
                                options:UIViewAnimationOptionCurveEaseInOut
                             animations:^{

                // 曲線點(r5點)是一個view.因此在block中有彈簧效果.而後根據他的動效路徑,在calculatePath中計算彈性圖形的形狀
                _curveView.frame = CGRectMake(SYS_DEVICE_WIDTH/2.0, MIN_HEIGHT, 3, 3);
                
            } completion:^(BOOL finished) {
                
                if(finished)
                {
                    _displayLink.paused = YES;
                    _isAnimating = NO;
                }
                
            }];
        }
    }
}
  • 根據r5的位置,更新_shapeLayer形狀
- (void)updateShapeLayerPath
{
    // 更新_shapeLayer形狀
    UIBezierPath *tPath = [UIBezierPath bezierPath];
    [tPath moveToPoint:CGPointMake(0, 0)];  //r1點
    [tPath addLineToPoint:CGPointMake(SYS_DEVICE_WIDTH, 0)];// r2點
    [tPath addLineToPoint:CGPointMake(SYS_DEVICE_WIDTH,  MIN_HEIGHT)]; //r4點
    [tPath addQuadCurveToPoint:CGPointMake(0, MIN_HEIGHT)
                  controlPoint:CGPointMake(_curveX, _curveY)]; // r3,r4,r5肯定的一個弧線
    [tPath closePath];
    _shapeLayer.path = tPath.CGPath;
}
  • 計算彈簧效果座標
- (void)calculatePath
{
    // 因爲手勢結束時,r5執行了一個UIView的彈簧動畫,把這個過程的座標記錄下來,並相應的畫出_shapeLayer形狀
    CALayer *layer = _curveView.layer.presentationLayer;
    _curveX = layer.position.x;
    _curveY = layer.position.y;1. - 1. - 這裏是列表文本
    [self updateShapeLayerPath];
}

總結


  • r5點的做用很是重要,由於直接對CAShapeLayer實現動效不太好實現。因此經過對r5點實現彈簧動效,記錄r5點的座標,再用UIBezierPath造成路徑,最後賦予CAShapeLayer,間接的完成了CAShapeLayer的彈簧動效。動畫

  • 若是你有疑問或者發現錯誤請留言給我。3Q~~code

相關文章
相關標籤/搜索