序
最近在網(wǎng)上看到一個(gè)很酷的下拉刷新效果(http://iostuts.io/2015/10/17/elastic-bounce-using-uibezierpath-and-pan-gesture/)扇售。自己試著實(shí)現(xiàn)了一下其中的果凍回彈效果。
效果
DEMO
- 由于文筆不太好-.- 嚣艇,建議先下載demo,再結(jié)合下面的分析承冰,會(huì)好理解點(diǎn)。地址https://github.com/Resory/RYCuteView
邏輯
下圖p1,藍(lán)色部分圖形是一個(gè)CAShapeLayer,他的形狀由UIBezierPath的路徑組成的食零。
這個(gè)路徑是由r1,r2,r3,r4,r5這5個(gè)紅點(diǎn)確定的困乒。其中r1,r2,r3,r4都是不動(dòng)點(diǎn),
唯一可以動(dòng)的是r5點(diǎn)
贰谣。根據(jù)上面的動(dòng)態(tài)圖可以看出,
CAShapeLayer的形狀是隨著r5紅點(diǎn)的移動(dòng)而相應(yīng)變化的
顶燕,所以只要獲得r5的坐標(biāo)變化就可以用UIBezierPath做出相應(yīng)的路徑凑保,然后就可以形成相應(yīng)的形狀。
實(shí)現(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點(diǎn)
- (void)configCurveView
{
// _curveView就是r5點(diǎn)
_curveX = SYS_DEVICE_WIDTH/2.0; // r5點(diǎn)x坐標(biāo)
_curveY = MIN_HEIGHT; // r5點(diǎn)y坐標(biāo)
_curveView = [[UIView alloc] initWithFrame:CGRectMake(_curveX, _curveY, 3, 3)];
_curveView.backgroundColor = [UIColor redColor];
[self addSubview:_curveView];
}
-
添加移動(dòng)手勢&CADisplayLink
- (void)configAction
{
_mHeight = 100; // 手勢移動(dòng)時(shí)相對(duì)高度
_isAnimating = NO; // 是否處于動(dòng)效狀態(tài)
// 手勢
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanAction:)];
self.userInteractionEnabled = YES;
[self addGestureRecognizer:pan];
// calculatePath方法是算出在運(yùn)行期間_curveView的坐標(biāo)涌攻,從而確定_shapeLayer的形狀
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(calculatePath)];
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// 在手勢結(jié)束的時(shí)候才調(diào)用calculatePath方法欧引,所以一開始是暫停的
_displayLink.paused = YES;
}
-
手勢解析
- 手勢移動(dòng)時(shí),r5紅點(diǎn)跟著手勢移動(dòng)恳谎,_shapeLayer則根據(jù)r5的坐標(biāo)來擴(kuò)大自己的區(qū)域
- 手勢結(jié)束時(shí)芝此,r5紅點(diǎn)通過UIView的動(dòng)畫方法來改變r(jià)5的坐標(biāo),同時(shí)_shapeLayer根據(jù)r5的坐標(biāo)縮小自己的區(qū)域并最終返回原形。
- (void)handlePanAction:(UIPanGestureRecognizer *)pan
{
if(!_isAnimating)
{
if(pan.state == UIGestureRecognizerStateChanged)
{
// 手勢移動(dòng)時(shí)因痛,_shapeLayer跟著手勢向下擴(kuò)大區(qū)域
CGPoint point = [pan translationInView:self];
// 這部分代碼使r5紅點(diǎn)跟著手勢走
_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坐標(biāo),更新_shapeLayer形狀
[self updateShapeLayerPath];
}
else if (pan.state == UIGestureRecognizerStateCancelled ||
pan.state == UIGestureRecognizerStateEnded ||
pan.state == UIGestureRecognizerStateFailed)
{
// 手勢結(jié)束時(shí),_shapeLayer返回原狀并產(chǎn)生彈簧動(dòng)效
_isAnimating = YES;
_displayLink.paused = NO; //開啟displaylink,會(huì)執(zhí)行方法calculatePath.
// 彈簧動(dòng)效
[UIView animateWithDuration:1.0
delay:0.0
usingSpringWithDamping:0.5
initialSpringVelocity:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
// 曲線點(diǎn)(r5點(diǎn))是一個(gè)view.所以在block中有彈簧效果.然后根據(jù)他的動(dòng)效路徑,在calculatePath中計(jì)算彈性圖形的形狀
_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點(diǎn)
[tPath addLineToPoint:CGPointMake(SYS_DEVICE_WIDTH, 0)];// r2點(diǎn)
[tPath addLineToPoint:CGPointMake(SYS_DEVICE_WIDTH, MIN_HEIGHT)]; //r4點(diǎn)
[tPath addQuadCurveToPoint:CGPointMake(0, MIN_HEIGHT)
controlPoint:CGPointMake(_curveX, _curveY)]; // r3,r4,r5確定的一個(gè)弧線
[tPath closePath];
_shapeLayer.path = tPath.CGPath;
}
-
計(jì)算彈簧效果坐標(biāo)
- (void)calculatePath
{
// 由于手勢結(jié)束時(shí),r5執(zhí)行了一個(gè)UIView的彈簧動(dòng)畫,把這個(gè)過程的坐標(biāo)記錄下來,并相應(yīng)的畫出_shapeLayer形狀
CALayer *layer = _curveView.layer.presentationLayer;
_curveX = layer.position.x;
_curveY = layer.position.y;
[self updateShapeLayerPath];
}
末
r5點(diǎn)的作用非常重要婚苹,因?yàn)橹苯訉?duì)CAShapeLayer實(shí)現(xiàn)動(dòng)效不太好實(shí)現(xiàn)。所以通過對(duì)r5點(diǎn)實(shí)現(xiàn)彈簧動(dòng)效鸵膏,記錄r5點(diǎn)的坐標(biāo)膊升,再用UIBezierPath形成路徑,最后賦予CAShapeLayer谭企,間接的完成了CAShapeLayer的彈簧動(dòng)效廓译。
如果你有疑問或者發(fā)現(xiàn)錯(cuò)誤請(qǐng)留言給我。3Q~~