Recently saw a cool drop-down refresh effect on the Web (http://iostuts.io/2015/10/17/elastic-bounce-using-uibezierpath-and-pan-gesture/). I tried to achieve the jelly rebound effect.
Effect
DEMO
Because the writing is not very good-.-, it is recommended to download the demo first, combined with the following analysis, will be good to understand the point. Address Https://github.com/Resory/RYCuteView
Logic
P1, the blue part of the graphic is a cashapelayer, whose shape is made up of Uibezierpath's path.
This path is determined by the 5 red dots of R1,R2,R3,R4,R5. Where the r1,r2,r3,r4 are fixed points, 唯一可以动的是r5点
.
According to the above dynamic diagram can be seen, CAShapeLayer的形状是随着r5红点的移动而相应变化的
so as long as the coordinate changes can be obtained R5 with Uibezierpath to make the corresponding path, and then you can form the corresponding shape.
Realize
123456 |
- (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]; } |
123456789 |
- (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];
}
|
- Add a move gesture &cadisplaylink
12345678910111213141516 |
- (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;
}
|
- Gesture parsing
When the gesture moves, the R5 red dot moves with the gesture, and _shapelayer expands its area according to the coordinates of the R5.
At the end of the gesture, the R5 red dot changes the coordinates of the R5 by means of UIView animation, while _shapelayer shrinks its area according to the R5 coordinates and eventually returns to its original shape.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 |
- (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);
// 根据r5坐标,更新_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;
}
}];
}
}
}
|
- Update the _shapelayer shape based on the location of the R5
123456789101112 |
- (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;
}
|
- Calculate the spring effect coordinates
12345678 |
- (void)calculatePath { // 由于手势结束时,r5执行了一个UIView的弹簧动画,把这个过程的坐标记录下来,并相应的画出_shapeLayer形状 CALayer *layer = _curveView.layer.presentationLayer; _curveX = layer.position.x; _curveY = layer.position.y; [self updateShapeLayerPath]; } |
Late
The role of R5 point is very important, because it is not very good to realize the effect of cashapelayer directly. So through the R5 point to achieve the spring dynamic effect, record the coordinates of the R5 point, and then use the Uibezierpath to form a path, finally given Cashapelayer, the indirect completion of the cashapelayer of the spring dynamic effect.
Link:
IOS-use Uibezierpath to achieve jelly effects
Elastic View animation using Uibezierpath
IOS-use Uibezierpath to achieve jelly effects