您的位置:首页 > 移动开发 > IOS开发

iOS粘性拖拽红点动画研究

2016-01-29 16:01 513 查看
 手机QQ有个针对强迫症很好的功能,就是未读消息红点的拖拽效果。下面我就来讲解要如何来实现这种效果。

            


一、预备知识

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: