浅谈贝塞尔曲线以及iOS中粘性动画的实现
关于贝塞尔曲线,网上相关的文章很多,这里我主要想用更简单的方法让大家理解贝塞尔曲线,当然,这仅仅是我个人的理解,如有错误的地方还请大家能够帮忙指出来,这样大家才能一起进步。
贝塞尔曲线,常用到的可分为如下几类,1阶曲线,2阶曲线(二次函数算是一种),3阶曲线,高阶曲线。
通用的方程为
这是由p0~pn这n+1个点组成的高阶方程。
但是光看这个方程的话或许大家会觉得不太理解,这东西到底能做什么?
我先逐渐的从1阶曲线讲起吧:
这里借鉴下这篇文章的几幅图片来描绘一下下列几个情况:
1阶曲线,是由两个端点组成,无控制点,此时贝塞尔曲线的起点就从端点开始出发,走到另一个端点,而此区间没有控制点,所以描绘出来的是一条直线。
2阶曲线,则是由两个端点加上一个控制点组成,此时,将两个端点分别与控制点连线所产生的就有两条线段,而分别从两条线段上的任意两点又能构成一条新的直线,不过此时我们不会取任意两点,在上图中是p0-p1形成的折线以及p1-p2形成的折线,如此一来,由p0-p1上的点以及p1-p2上的点同时出发的话这两条线段上的点会同时动,由这两个动点所产生的直线是一条会变化的直线,而这条直线也正是贝塞尔曲线的点在描绘贝塞尔曲线的时候所要经过的点。正因为直线式不断变化的,所以它描绘出来的线就变成了一条平滑的曲线。
3阶曲线以及高阶曲线,同2阶曲线一样,2个或2个以上的控制点所构成的所有折线段的点分别出发,然后在将这些动点所形成的直线再分解成n-1个动的折线,再将折线上的点再重新链接,如此递归,最终转换成1阶,如此一来可描绘出n阶贝塞尔曲线。
好了这里把大家给说晕了,先来看看实际效果吧
这是我最近做的一个开源,是仿照美团外卖来做的一个下拉动画,可以看到这个粘性动画就是当我们滑动tableview从而产生的一个动画,这里我谈一谈我的实现思路吧,你所看到的这个粘性红色视图其实是用贝塞尔曲线描绘出来的闭合图形,我们可以很直观的可以看出它其实是一个二阶贝塞尔曲线,而且控制点为整个屏幕的中心,通过获取scrollview的滑动偏移量来绘制控制点的坐标。
下面来看一下代码:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetY = scrollView.contentOffset.y+64;
refreshHeadView.offsetY = offsetY;
//异步执行,setNeedsDisplay会调用自动调用drawRect方法
[refreshHeadView setNeedsDisplay];
}
我们通过scrollview的代理方法获取到偏移量存了起来,然后在UIView中进行绘制,setNeedsDisplay这个方法会使得UIView重绘,
#pragma mark 画图
- (void)drawRect:(CGRect)rect {
// 创建一个贝塞尔曲线句柄
UIBezierPath *path = [UIBezierPath bezierPath];
// 初始化该path到一个初始点
[path moveToPoint:CGPointMake(0, 0)];
// // 添加一条直线
// [path addLineToPoint:CGPointMake(0, 0)];
// 画二元曲线,一般和moveToPoint配合使用
[path addQuadCurveToPoint:CGPointMake(self.frame.size.width, 0) controlPoint:CGPointMake(self.frame.size.width/2,- _offsetY*1.2)];
// 关闭该path
[path closePath];
// 创建描边(Quartz)上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 将此path添加到Quartz上下文中
CGContextAddPath(context, path.CGPath);
// 设置本身颜色
[[UIColor redColor] set];
// 设置填充的路径
CGContextFillPath(context);
}
这样当我滑动的时候,控制点的坐标也在改变,且在屏幕中心向下移动,就构成了我们熟知的二次曲线。下面附上GitHub地址