序
最近在網(wǎng)上看到一個很酷的下拉刷新效果(http://iostuts.io/2017/10/17/elastic-bounce-using-uibezierpath-and-pan-gesture/)验残。自己試著實現(xiàn)了一下其中的果凍回彈效果。
效果
DEMO由于文筆不太好-.- 督怜,建議先下載demo,再結(jié)合下面的分析先紫,會好理解點巷燥。地址https://github.com/Resory/RYCuteView邏輯
下圖p1,藍色部分圖形是一個CAShapeLayer,他的形狀由UIBezierPath的路徑組成的候址。
這個路徑是由r1,r2,r3,r4,r5這5個紅點確定的。其中r1,r2,r3,r4都是不動點庭敦,唯一可以動的是r5點
匿情。
根據(jù)上面的動態(tài)圖可以看出,CAShapeLayer的形狀是隨著r5紅點的移動而相應變化的
兰迫,所以只要獲得r5的坐標變化就可以用UIBezierPath做出相應的路徑,然后就可以形成相應的形狀炬称。
實現(xiàn)初始化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; // 是否處于動效狀態(tài) // 手勢 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]; // 在手勢結(jié)束的時候才調(diào)用calculatePath方法,所以一開始是暫停的 _displayLink.paused = YES; }
手勢解析手勢移動時玲躯,r5紅點跟著手勢移動据德,_shapeLayer則根據(jù)r5的坐標來擴大自己的區(qū)域 手勢結(jié)束時,r5紅點通過UIView的動畫方法來改變r5的坐標,同時_shapeLayer根據(jù)r5的坐標縮小自己的區(qū)域并最終返回原形跷车。
- (void)handlePanAction:(UIPanGestureRecognizer *)pan{ if(!_isAnimating) { if(pan.state == UIGestureRecognizerStateChanged) { // 手勢移動時棘利,_shapeLayer跟著手勢向下擴大區(qū)域 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); // 根據(jù)r5坐標,更新_shapeLayer形狀 [self updateShapeLayerPath]; } else if (pan.state == UIGestureRecognizerStateCancelled || pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateFailed) { // 手勢結(jié)束時,_shapeLayer返回原狀并產(chǎn)生彈簧動效 _isAnimating = YES; _displayLink.paused = NO; //開啟displaylink,會執(zhí)行方法calculatePath. // 彈簧動效 [UIView animateWithDuration:1.0 delay:0.0 usingSpringWithDamping:0.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ // 曲線點(r5點)是一個view.所以在block中有彈簧效果.然后根據(jù)他的動效路徑,在calculatePath中計算彈性圖形的形狀 _curveView.frame = CGRectMake(SYS_DEVICE_WIDTH/2.0, MIN_HEIGHT, 3, 3); } completion:^(BOOL finished) { if(finished) { _displayLink.paused = YES; _isAnimating = NO; } }]; } }}
根據(jù)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{ // 由于手勢結(jié)束時,r5執(zhí)行了一個UIView的彈簧動畫,把這個過程的坐標記錄下來,并相應的畫出_shapeLayer形狀 CALayer *layer = _curveView.layer.presentationLayer; _curveX = layer.position.x; _curveY = layer.position.y; [self updateShapeLayerPath];}
末
r5點的作用非常重要,因為直接對CAShapeLayer實現(xiàn)動效不太好實現(xiàn)朽缴。所以通過對r5點實現(xiàn)彈簧動效善玫,記錄r5點的坐標,再用UIBezierPath形成路徑密强,最后賦予CAShapeLayer茅郎,間接的完成了CAShapeLayer的彈簧動效。
如果你有疑問或者發(fā)現(xiàn)錯誤請留言給我或渤。3Q~~