iOS粘性拖拽红点动画研究
2016-01-29 16:01
513 查看
手机QQ有个针对强迫症很好的功能,就是未读消息红点的拖拽效果。下面我就来讲解要如何来实现这种效果。
一、预备知识
1.CAShapeLayer
CAShapeLayer是CALayer的子类,它的特别之处在于它的形状可以通过贝塞尔曲线任意定制。
ex:
2.UIBezierpath
为了实现拖拽的效果,我们需要用到UIBezierPath类来绘制曲线。UIBezierPath类可以用于创建基于矢量的路径,这个类在UIKit中。此类是Core Graphics框架关于path的一个封装。使用它可以定义简单的形状,如椭圆或者矩形,或者有多个直线和曲线段组成的形状。
ex:
在UIBezierPath中有两个绘制曲线(curve)的API:
这两个方法的区别在于控制点的个数不同,控制点是用来描述曲线弯曲方向的点,如下图的P1。具体的数学解释可以阅读这篇博客。
下图左侧是两个控制点的效果,右侧是单个控制点的。当然为了让形如右侧的曲线表现的更加平滑自然,使用多个控制点绘制一段单弧曲线也未尝不可。
二、总体实现方法
1.CAShapeLayer方法
这种方法的不同之处在于 中间绿色的部分是使用CAShapeLayer来完成,蓝色的圆点和红色的圆点都是两个独立的view。
下面我将针对CAShapeLayer这种实现方法来进行解释。
二、形变的计算
使用CAShapeLayer实现起来整体并没有太大的障碍,难点仅在于贝塞尔曲线上各个点的计算。
1.二阶贝塞尔曲线
如下图所示,使OA ⊥ AB, PB ⊥ AB ,且 OA=PB=d/2.(你也可以自己定制角DAB的角度与OA、PB的长度,不过这样做最方便也比较美观) 这就转化为了一道高中数学题,已知的是S点、T点的坐标,r1 , r2; 进而能够得到ST的长度,与θ的sin与cos值。向右延长y1T,可知角ATy1=θ,其它同理。有了这些我们就可以轻松求得点A、B、C、D、O、P的坐标。
(图片来自网络)
转化为代码:
2.三阶贝塞尔曲线
如果二阶曲线并不能满足红点的“弹性”感,我们不妨用上面的方式,来计算出三阶贝塞尔曲线两个控制点的坐标。为了看清各个角度的关系,我画了下面这张图
(上图说明:QA⊥AT , O1T1⊥AT)
首先,我们要给出一些控制点的设置,我们设∠QAO1为α,这个α可以代表曲线在第一个控制点的弯曲程度;设 O1T1 = ST/4,O2T1 = ST/2,1/4和1/2这两个分数可以抽象的想成是曲线弯曲的位置。(ST即为手指触点与小圆圆心之间的距离)
好了,现在我们就可以开始计算O1点的坐标了。通过上文的方法,我们已经可以得出A点的坐标。现在列出我们的已知的条件:
A.x,A.y;
O1T1 = ST/4;
∠AO1T1 = ∠QAO1 = α;
α已知;ST已知;
那么就可以得出:
AO1 = O1T1 */cos(α) = ST/4/cos(α);
O1.x = A.x + AO1 * sin(α+θ);
O1.y = A.y - AO1 * cos(α+θ);(这里是减号,注意iOS上的坐标系与我画的不同)
再将(α+θ)的三角函数展开,我们就得到了点O1的坐标。
转化为代码:
下面计算点O2的坐标。
貌似∠O2AT1不太好搞,那么我们先设它为β。有了上面求O1点的经验,可以知道只要知道∠A O2 Xo2的三角函数就能够得到O2的坐标了。
已知条件: O2T1, AT1;(上文通过α计算过了)
O2T1 ⊥ AT1;
∠θ的三角函数已知;
那么可以得到:
O2T1 = ST/2;
O2A = √ ̄((AT1)² +(O2T1)²);(勾股定理)
O2.x = A.x + O2A * sin(β+θ);
O2.y = A.y - O2A * cos(β+θ);
代码:
这样我们就得到了O2的坐标~
这样我们就可以绘制曲线了:
至此,革命尚未成功,因为我们只画了一边的曲线。同理我们也可以计算出另一边的控制点,而且由于对称性,左边计算好的β的三角函数值是可以直接拿来用的,只不过右侧的角度不是β+θ,而是θ-β。
四、总结
类似的粘性动画还有很多,红点拖拽效果只是其中一种。二阶贝塞尔曲线的计算是学习自github的大神,通过他的思路我实现了三阶贝塞尔曲线的效果。
如有不足与错误还请指出。demo的github链接:https://github.com/MeowWang/RedDotDrag,欢迎大家下载。
原项目的github链接:https://github.com/KittenYang/KYCuteView
一、预备知识
1.CAShapeLayer
CAShapeLayer是CALayer的子类,它的特别之处在于它的形状可以通过贝塞尔曲线任意定制。
ex:
cutePath = [UIBezierPath bezierPath]; shapeLayer.path = [cutePath CGPath];
2.UIBezierpath
为了实现拖拽的效果,我们需要用到UIBezierPath类来绘制曲线。UIBezierPath类可以用于创建基于矢量的路径,这个类在UIKit中。此类是Core Graphics框架关于path的一个封装。使用它可以定义简单的形状,如椭圆或者矩形,或者有多个直线和曲线段组成的形状。
ex:
cutePath = [UIBezierPath bezierPath]; [cutePath moveToPoint:pointA]; [cutePath addCurveToPoint:pointD controlPoint1:pointO1 controlPoint2:pointO2]; [cutePath addLineToPoint:pointC]; [cutePath addCurveToPoint:pointB controlPoint1:pointP2 controlPoint2:pointP1]; [cutePath moveToPoint:pointA];
在UIBezierPath中有两个绘制曲线(curve)的API:
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2; //三阶贝塞尔曲线 - (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint; //二阶贝塞尔曲线
这两个方法的区别在于控制点的个数不同,控制点是用来描述曲线弯曲方向的点,如下图的P1。具体的数学解释可以阅读这篇博客。
下图左侧是两个控制点的效果,右侧是单个控制点的。当然为了让形如右侧的曲线表现的更加平滑自然,使用多个控制点绘制一段单弧曲线也未尝不可。
二、总体实现方法
1.CAShapeLayer方法
这种方法的不同之处在于 中间绿色的部分是使用CAShapeLayer来完成,蓝色的圆点和红色的圆点都是两个独立的view。
下面我将针对CAShapeLayer这种实现方法来进行解释。
二、形变的计算
使用CAShapeLayer实现起来整体并没有太大的障碍,难点仅在于贝塞尔曲线上各个点的计算。
1.二阶贝塞尔曲线
如下图所示,使OA ⊥ AB, PB ⊥ AB ,且 OA=PB=d/2.(你也可以自己定制角DAB的角度与OA、PB的长度,不过这样做最方便也比较美观) 这就转化为了一道高中数学题,已知的是S点、T点的坐标,r1 , r2; 进而能够得到ST的长度,与θ的sin与cos值。向右延长y1T,可知角ATy1=θ,其它同理。有了这些我们就可以轻松求得点A、B、C、D、O、P的坐标。
(图片来自网络)
转化为代码:
cosDigree = (y1-y2)/centerDistance; sinDigree = (x2-x1)/centerDistance; pointA = CGPointMake(x1-r1*cosDigree, y1-r1*sinDigree); // A pointB = CGPointMake(x1+r1*cosDigree, y1+r1*sinDigree); // B pointD = CGPointMake(x2-r2*cosDigree, y2-r2*sinDigree); // D pointC = CGPointMake(x2+r2*cosDigree, y2+r2*sinDigree); // C //二阶贝塞尔曲线控制点 pointO = CGPointMake(pointA.x + (centerDistance / 2)*sinDigree, pointA.y + (centerDistance / 2)*cosDigree); pointP = CGPointMake(pointB.x + (centerDistance / 2)*sinDigree, pointB.y + (centerDistance / 2)*cosDigree); cutePath = [UIBezierPath bezierPath]; //二次贝塞尔曲线 [cutePath moveToPoint:pointA]; [cutePath addQuadCurveToPoint:pointD controlPoint:pointO]; [cutePath addLineToPoint:pointC]; [cutePath addQuadCurveToPoint:pointB controlPoint:pointP]; [cutePath moveToPoint:pointA]; shapeLayer.path = [cutePath CGPath]; shapeLayer.fillColor = [UIColor greenColor].CGColor;
2.三阶贝塞尔曲线
如果二阶曲线并不能满足红点的“弹性”感,我们不妨用上面的方式,来计算出三阶贝塞尔曲线两个控制点的坐标。为了看清各个角度的关系,我画了下面这张图
(上图说明:QA⊥AT , O1T1⊥AT)
首先,我们要给出一些控制点的设置,我们设∠QAO1为α,这个α可以代表曲线在第一个控制点的弯曲程度;设 O1T1 = ST/4,O2T1 = ST/2,1/4和1/2这两个分数可以抽象的想成是曲线弯曲的位置。(ST即为手指触点与小圆圆心之间的距离)
好了,现在我们就可以开始计算O1点的坐标了。通过上文的方法,我们已经可以得出A点的坐标。现在列出我们的已知的条件:
A.x,A.y;
O1T1 = ST/4;
∠AO1T1 = ∠QAO1 = α;
α已知;ST已知;
那么就可以得出:
AO1 = O1T1 */cos(α) = ST/4/cos(α);
O1.x = A.x + AO1 * sin(α+θ);
O1.y = A.y - AO1 * cos(α+θ);(这里是减号,注意iOS上的坐标系与我画的不同)
再将(α+θ)的三角函数展开,我们就得到了点O1的坐标。
转化为代码:
cosDigree = (y1-y2)/centerDistance; sinDigree = (x2-x1)/centerDistance; cosAO1T1 = cos(BezierAngle * M_PI/180); sinAO1T1 = sin(BezierAngle * M_PI/180); cosO1 = cosDigree * cosAO1T1 - sinDigree * sinAO1T1; sinO1 = sinDigree * cosAO1T1 + cosDigree * sinAO1T1; pointO1 = CGPointMake(pointA.x + centerDistance*sinO1/(4*cosAO1T1), pointA.y-centerDistance*cosO1/(4*cosAO1T1));
下面计算点O2的坐标。
貌似∠O2AT1不太好搞,那么我们先设它为β。有了上面求O1点的经验,可以知道只要知道∠A O2 Xo2的三角函数就能够得到O2的坐标了。
已知条件: O2T1, AT1;(上文通过α计算过了)
O2T1 ⊥ AT1;
∠θ的三角函数已知;
那么可以得到:
O2T1 = ST/2;
O2A = √ ̄((AT1)² +(O2T1)²);(勾股定理)
O2.x = A.x + O2A * sin(β+θ);
O2.y = A.y - O2A * cos(β+θ);
代码:
//角β AO2 = sqrt(pow(O2T1, 2) + pow(AT1, 2)); cosAO2T1 = O2T1/AO2; sinAO2T1 = AT1/AO2; cosO2 = cosAO2T1 * cosDigree - sinAO2T1 * sinDigree; sinO2 = sinDigree * cosAO2T1 + cosDigree * sinAO2T1; pointO2 = CGPointMake(pointA.x + AO2 * sinO2, pointA.y - AO2*cosO2);
这样我们就得到了O2的坐标~
这样我们就可以绘制曲线了:
[cutePath addCurveToPoint:pointD controlPoint1:pointO1 controlPoint2:pointO2];
至此,革命尚未成功,因为我们只画了一边的曲线。同理我们也可以计算出另一边的控制点,而且由于对称性,左边计算好的β的三角函数值是可以直接拿来用的,只不过右侧的角度不是β+θ,而是θ-β。
四、总结
类似的粘性动画还有很多,红点拖拽效果只是其中一种。二阶贝塞尔曲线的计算是学习自github的大神,通过他的思路我实现了三阶贝塞尔曲线的效果。
如有不足与错误还请指出。demo的github链接:https://github.com/MeowWang/RedDotDrag,欢迎大家下载。
原项目的github链接:https://github.com/KittenYang/KYCuteView
相关文章推荐
- iOS--新建工程需知
- 解决iOS开发中涉及到的retain cycle导致控制器无法释放的问题
- 用 NSURProtocol 注入测试数据
- iOS 项目中添加pch文件
- iOS:基于Socket的第三方框架CocoaAsyncSocket的使用
- 致2015最后2个月ios开发
- iOS加载Html文件时不能显示图片
- 分析iOS Crash文件:符号化iOS Crash文件的3种方法
- [IOS 开发] __block的用途和实现原理
- iOS开发~CocoaPods使用详细说明【转】
- iOS远程推送服务
- iOS 支付宝的使用
- iOS开发-Quartz2D绘制时定时器选择
- iOS发布应用透明图标转非透明图标
- [iOS]集成环信SDK然后运行时候crash了-[NSBundle initWithURL:]: nil URL argument'
- iOS获取设备唯一标识的各种方法?IDFA、IDFV、UDID分别是什么含义?
- ios高效开发-理解属性和正确的使用属性
- IOS 运行时 Runtime 今天开始学习
- iOS中tableview刷新某一行
- iOS清楚緩存