Android-贝塞尔曲线

Android 实战项目分享(一)用Android Studio绘制贝塞尔曲线的艺术之旅 通过使用Android Studio,我们实现了绘制贝塞尔曲线的功能,让你能够轻松创作出令人惊叹的艺术作品。不论你是热爱绘画的大学生还是渴望学习的艺术爱好者,这个应用程序将为你点燃创作的激情,让你沉浸在绘画的乐趣中。通用隐私协议服务协议弹出窗口:我们关注用户隐私权,为了保障你的合法权益,我们在应用程序中提供了通用隐私协议服务协议弹出窗口。这意味着在使用过程中,我们将确保你对所需权限的控制,保护你的个人信息和设备安全。,让Android Studio与你的艺术梦想相遇,一起创造属于你自己的独特世界~ 阅读详情

前言:

从去年开始了解贝塞尔曲线之后,发现开发中,不管是Android/iOS平台,还是web前端等,都有贝塞尔曲线的应用,通过绘制贝塞尔曲线,可以帮助开发者实现很多效果,例如一段时间内很流行的粘合型的下拉刷新、又如天气曲线图,同时,以贝塞尔曲线为基础的贝塞尔工具是所有绘图软件的最常用最实用的工具。


什么是贝塞尔曲线:

贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。主要结构:起始点、终止点(也称锚点)、控制点。通过调整控制点,贝塞尔曲线的形状会发生变化。


贝塞尔曲线的分类:    


一阶贝塞尔曲线(线段):

公式: 




意义:由 P0 至 P1 的连续点, 描述的一条线段


二阶贝塞尔曲线(抛物线):

公式: 





原理: 由 P0 至 P1 的连续点 Q0,描述一条线段。 
           由 P1 至 P2 的连续点 Q1,描述一条线段。 
           由 Q0 至 Q1 的连续点 B(t),描述一条二次贝塞尔曲线。


三阶贝塞尔曲线:




当然还有四阶曲线、五阶曲线......


一篇帮助理解贝塞尔曲线的文章:http://www.html-js.com/article/1628

一个帮助绘制贝塞尔曲线的网址:http://bezier.method.ac/#


贝塞尔曲线代码实现:

我们一般使用的是二阶贝塞尔曲线和三阶贝塞尔曲线,从动态图和公式我们可以看出,贝塞尔曲线主要由于三个部分控制:起点,终点,中间的辅助控制点。如何利用这三个点画出贝塞尔曲线,在android自带的Path类中自带了方法,可以帮助我们实现贝塞尔曲线:

  1. /** 
  2.  * Add a quadratic bezier from the last point, approaching control point 
  3.  * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for 
  4.  * this contour, the first point is automatically set to (0,0). 
  5.  * 
  6.  * @param x1 The x-coordinate of the control point on a quadratic curve 
  7.  * @param y1 The y-coordinate of the control point on a quadratic curve 
  8.  * @param x2 The x-coordinate of the end point on a quadratic curve 
  9.  * @param y2 The y-coordinate of the end point on a quadratic curve 
  10.  */  
  11. public void quadTo(float x1, float y1, float x2, float y2) {  
  12.     isSimplePath = false;  
  13.     native_quadTo(mNativePath, x1, y1, x2, y2);  
  14. }  


quadTo()方法从上一个点为起点开始绘制贝塞尔曲线,其中(x1,y1)为辅助控制点,(x2,y2)为终点。

Path mPath = new Path();
mPath.moveTo(x0,y0);
mPath.quadTo(x1,y1,x2,y2);

如调用以上代码,即绘制起点(x0,y0),终点(x2,y2),辅助控制点(x1,y1)的贝塞尔曲线。因此,通过不断改变这三个点的位置,我们可以绘制出各种各样的曲线



  1. /** 
  2.  * Add a cubic bezier from the last point, approaching control points 
  3.  * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been 
  4.  * made for this contour, the first point is automatically set to (0,0). 
  5.  * 
  6.  * @param x1 The x-coordinate of the 1st control point on a cubic curve 
  7.  * @param y1 The y-coordinate of the 1st control point on a cubic curve 
  8.  * @param x2 The x-coordinate of the 2nd control point on a cubic curve 
  9.  * @param y2 The y-coordinate of the 2nd control point on a cubic curve 
  10.  * @param x3 The x-coordinate of the end point on a cubic curve 
  11.  * @param y3 The y-coordinate of the end point on a cubic curve 
  12.  */  
  13. public void cubicTo(float x1, float y1, float x2, float y2,  
  14.                     float x3, float y3) {  
  15.     isSimplePath = false;  
  16.     native_cubicTo(mNativePath, x1, y1, x2, y2, x3, y3);  
  17. }  

cubicTo()方法从上一个点为起点开始绘制三阶贝塞尔曲线,其中(x1,y1),( x2, y2 )为辅助控制点,(x3,y3)为终点。


贝塞尔曲线的应用

(1)二阶贝塞尔曲线——波浪

要实现一个波浪不断涌动的效果,这种效果在很多手机应用中可见,例如手机电量,内存剩余等。我们需要绘制带有平滑自然效果的曲线,这时候就需要贝塞尔曲线来辅助了。

 

动态图:

    

   

原理图:


   


         

图中的矩阵即为视图的可见范围,也就是我们手机常见的区域。通过属性动画类ValueAnimator不断改变点1的横坐标,随着点1横坐标向右移动,点2,点3,点4,点5,以及四个控制点的坐标随着点1的移动同时位移相同距离,每一次坐标点更新,我们调用一次invalidate()方法,调用draw重新绘制视图,绘制四段贝塞尔曲线。最后点1移动到原先点3的位置,这样就完成了一次动画。

  

这样,通过循环不断的动画效果,我们就实现了波浪的效果。

        

        onDraw() 代码:

  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     if (!mIsRunning || !mHasInit)  
  5.         return;  
  6.     mPath.reset();  
  7.     mPath.moveTo(mLeft1.x, mLeft1.y);  
  8.     mPath.quadTo(mControlLeft1.x, mControlLeft1.y, mLeft2.x, mLeft2.y);  
  9.     mPath.quadTo(mControlLeft2.x, mControlLeft2.y, mFirst.x, mFirst.y);  
  10.     mPath.quadTo(mControlFirst.x, mControlFirst.y, mSecond.x, mSecond.y);  
  11.     mPath.quadTo(mControlSecond.x, mControlSecond.y, mRight.x, mRight.y);  
  12.     mPath.lineTo(mRight.x, mHeight);  
  13.     mPath.lineTo(mLeft1.x, mHeight);  
  14.     mPath.lineTo(mLeft1.x, mLeft1.y);  
  15.     canvas.drawPath(mPath, mPaint);  
  16. }  

     

(2)二阶贝塞尔曲线——粘连体

利用二阶贝塞尔曲线还可以实现,类似两种物体粘合在一起的效果,比如我们常用的qq,在qq聊天列表上有一个非常有意思的功能,就是当我们用手指移动聊天列表上的未读消息标志的时候,它与聊天列表会产生粘连的效果:

    

    

     现在,我们来模拟这种效果,利用学到的二阶贝塞尔曲线来绘制。


     

   

我们看到原理图,基本构造为两个圆,和两端贝塞尔曲线,绘制贝塞尔曲线,由于这是一个二阶的贝塞尔曲线,我们只需要一个控制点,在这个图里,我们的两条贝塞尔曲线的两个控制点分别为(x1,y1)(x4, y4)的中点,(x2, y2)(x3, y3)的中点。

从图中可以看出,我们的贝塞尔曲线由我们的控制点控制,控制点又是被起点和终点控制着,因此,当两个圆距离越大,曲线越趋于平缓,当两个圆距离越小,曲线的波动度越大,这样,我们想要的粘连的效果就实现了。另外,这里有一个还有角度(图中的45度角)可以用来控制,也可以作为控制曲线波动度的参数。 

 通过以上分析,我们通过一个方法来绘制两个圆之间的粘连体路径:

    

        

  1. /** 
  2.  * 画粘连体 
  3.  * @param cx1     圆心x1 
  4.  * @param cy1     圆心y1 
  5.  * @param r1      圆半径r1 
  6.  * @param offset1 贝塞尔曲线偏移角度offset1 
  7.  * @param cx2     圆心x2 
  8.  * @param cy2     圆心y2 
  9.  * @param r2      圆半径r2 
  10.  * @param offset2 贝塞尔曲线偏移角度offset2 
  11.  * @return 
  12.  */  
  13. public static Path drawAdhesionBody(float cx1, float cy1, float r1, float offset1, float   
  14.         cx2, float cy2, float r2, float offset2) {  
  15.       
  16.     /* 求三角函数 */  
  17.     float degrees =(float) Math.toDegrees(Math.atan(Math.abs(cy2 - cy1) / Math.abs(cx2 - cx1)));  
  18.       
  19.     /* 根据圆1与圆2的相对位置求四个点 */  
  20.     float differenceX = cx1 - cx2;  
  21.     float differenceY = cy1 - cy2;  
  22.   
  23.     /* 两条贝塞尔曲线的四个端点 */  
  24.     float x1,y1,x2,y2,x3,y3,x4,y4;  
  25.       
  26.     /* 圆1在圆2的下边 */  
  27.     if (differenceX == 0 && differenceY > 0) {  
  28.         x2 = cx2 - r2 * (float) Math.sin(Math.toRadians(offset2));  
  29.         y2 = cy2 + r2 * (float) Math.cos(Math.toRadians(offset2));  
  30.         x4 = cx2 + r2 * (float) Math.sin(Math.toRadians(offset2));  
  31.         y4 = cy2 + r2 * (float) Math.cos(Math.toRadians(offset2));  
  32.         x1 = cx1 - r1 * (float) Math.sin(Math.toRadians(offset1));  
  33.         y1 = cy1 - r1 * (float) Math.cos(Math.toRadians(offset1));  
  34.         x3 = cx1 + r1 * (float) Math.sin(Math.toRadians(offset1));  
  35.         y3 = cy1 - r1 * (float) Math.cos(Math.toRadians(offset1));  
  36.     }  
  37.     /* 圆1在圆2的上边 */  
  38.     else if (differenceX == 0 && differenceY < 0) {  
  39.         x2 = cx2 - r2 * (float) Math.sin(Math.toRadians(offset2));  
  40.         y2 = cy2 - r2 * (float) Math.cos(Math.toRadians(offset2));  
  41.         x4 = cx2 + r2 * (float) Math.sin(Math.toRadians(offset2));  
  42.         y4 = cy2 - r2 * (float) Math.cos(Math.toRadians(offset2));  
  43.         x1 = cx1 - r1 * (float) Math.sin(Math.toRadians(offset1));  
  44.         y1 = cy1 + r1 * (float) Math.cos(Math.toRadians(offset1));  
  45.         x3 = cx1 + r1 * (float) Math.sin(Math.toRadians(offset1));  
  46.         y3 = cy1 + r1 * (float) Math.cos(Math.toRadians(offset1));  
  47.     }  
  48.     /* 圆1在圆2的右边 */  
  49.     else if (differenceX > 0 && differenceY == 0) {  
  50.         x2 = cx2 + r2 * (float) Math.cos(Math.toRadians(offset2));  
  51.         y2 = cy2 + r2 * (float) Math.sin(Math.toRadians(offset2));  
  52.         x4 = cx2 + r2 * (float) Math.cos(Math.toRadians(offset2));  
  53.         y4 = cy2 - r2 * (float) Math.sin(Math.toRadians(offset2));  
  54.         x1 = cx1 - r1 * (float) Math.cos(Math.toRadians(offset1));  
  55.         y1 = cy1 + r1 * (float) Math.sin(Math.toRadians(offset1));  
  56.         x3 = cx1 - r1 * (float) Math.cos(Math.toRadians(offset1));  
  57.         y3 = cy1 - r1 * (float) Math.sin(Math.toRadians(offset1));  
  58.     }   
  59.     /* 圆1在圆2的左边 */  
  60.     else if (differenceX < 0 && differenceY == 0 ) {  
  61.         x2 = cx2 - r2 * (float) Math.cos(Math.toRadians(offset2));  
  62.         y2 = cy2 + r2 * (float) Math.sin(Math.toRadians(offset2));  
  63.         x4 = cx2 - r2 * (float) Math.cos(Math.toRadians(offset2));  
  64.         y4 = cy2 - r2 * (float) Math.sin(Math.toRadians(offset2));  
  65.         x1 = cx1 + r1 * (float) Math.cos(Math.toRadians(offset1));  
  66.         y1 = cy1 + r1 * (float) Math.sin(Math.toRadians(offset1));  
  67.         x3 = cx1 + r1 * (float) Math.cos(Math.toRadians(offset1));  
  68.         y3 = cy1 - r1 * (float) Math.sin(Math.toRadians(offset1));  
  69.     }  
  70.     /* 圆1在圆2的右下角 */  
  71.     else if (differenceX > 0 && differenceY > 0) {  
  72.         x2 = cx2 - r2 * (float) Math.cos(Math.toRadians(180 - offset2 - degrees));  
  73.         y2 = cy2 + r2 * (float) Math.sin(Math.toRadians(180 - offset2 - degrees));  
  74.         x4 = cx2 + r2 * (float) Math.cos(Math.toRadians(degrees - offset2));  
  75.         y4 = cy2 + r2 * (float) Math.sin(Math.toRadians(degrees - offset2));  
  76.         x1 = cx1 - r1 * (float) Math.cos(Math.toRadians(degrees - offset1));  
  77.         y1 = cy1 - r1 * (float) Math.sin(Math.toRadians(degrees - offset1));  
  78.         x3 = cx1 + r1 * (float) Math.cos(Math.toRadians(180 - offset1 - degrees));  
  79.         y3 = cy1 - r1 * (float) Math.sin(Math.toRadians(180 - offset1 - degrees));  
  80.     }  
  81.     /* 圆1在圆2的左上角 */  
  82.     else if (differenceX < 0 && differenceY < 0) {  
  83.         x2 = cx2 - r2 * (float) Math.cos(Math.toRadians(degrees - offset2));  
  84.         y2 = cy2 - r2 * (float) Math.sin(Math.toRadians(degrees - offset2));  
  85.         x4 = cx2 + r2 * (float) Math.cos(Math.toRadians(180 - offset2 - degrees));  
  86.         y4 = cy2 - r2 * (float) Math.sin(Math.toRadians(180 - offset2 - degrees));  
  87.         x1 = cx1 - r1 * (float) Math.cos(Math.toRadians(180 - offset1 - degrees));  
  88.         y1 = cy1 + r1 * (float) Math.sin(Math.toRadians(180 - offset1 - degrees));  
  89.         x3 = cx1 + r1 * (float) Math.cos(Math.toRadians(degrees - offset1));  
  90.         y3 = cy1 + r1 * (float) Math.sin(Math.toRadians(degrees - offset1));  
  91.     }  
  92.     /* 圆1在圆2的左下角 */  
  93.     else if (differenceX < 0 && differenceY > 0) {  
  94.         x2 = cx2 - r2 * (float) Math.cos(Math.toRadians(degrees - offset2));  
  95.         y2 = cy2 + r2 * (float) Math.sin(Math.toRadians(degrees - offset2));  
  96.         x4 = cx2 + r2 * (float) Math.cos(Math.toRadians(180 - offset2 - degrees));  
  97.         y4 = cy2 + r2 * (float) Math.sin(Math.toRadians(180 - offset2 - degrees));  
  98.         x1 = cx1 - r1 * (float) Math.cos(Math.toRadians(180 - offset1 - degrees));  
  99.         y1 = cy1 - r1 * (float) Math.sin(Math.toRadians(180 - offset1 - degrees));  
  100.         x3 = cx1 + r1 * (float) Math.cos(Math.toRadians(degrees - offset1));  
  101.         y3 = cy1 - r1 * (float) Math.sin(Math.toRadians(degrees - offset1));  
  102.     }  
  103.     /* 圆1在圆2的右上角 */  
  104.     else {  
  105.         x2 = cx2 - r2 * (float) Math.cos(Math.toRadians(180 - offset2 - degrees));  
  106.         y2 = cy2 - r2 * (float) Math.sin(Math.toRadians(180 - offset2 - degrees));  
  107.         x4 = cx2 + r2 * (float) Math.cos(Math.toRadians(degrees - offset2));  
  108.         y4 = cy2 - r2 * (float) Math.sin(Math.toRadians(degrees - offset2));  
  109.         x1 = cx1 - r1 * (float) Math.cos(Math.toRadians(degrees - offset1));  
  110.         y1 = cy1 + r1* (float) Math.sin(Math.toRadians(degrees - offset1));  
  111.         x3 = cx1 + r1 * (float) Math.cos(Math.toRadians(180 - offset1 - degrees));  
  112.         y3 = cy1 + r1 * (float) Math.sin(Math.toRadians(180 - offset1 - degrees));  
  113.     }  
  114.       
  115.     /* 贝塞尔曲线的控制点 */  
  116.     float anchorX1,anchorY1,anchorX2,anchorY2;  
  117.       
  118.     /* 圆1大于圆2 */  
  119.     if (r1 > r2) {  
  120.         anchorX1 = (x2 + x3) / 2;  
  121.         anchorY1 = (y2 + y3) / 2;  
  122.         anchorX2 = (x1 + x4) / 2;  
  123.         anchorY2 = (y1 + y4) / 2;  
  124.     }  
  125.     /* 圆1小于或等于圆2 */  
  126.     else {  
  127.         anchorX1 = (x1 + x4) / 2;  
  128.         anchorY1 = (y1 + y4) / 2;  
  129.         anchorX2 = (x2 + x3) / 2;  
  130.         anchorY2 = (y2 + y3) / 2;  
  131.     }  
  132.       
  133.     /* 画粘连体 */  
  134.     Path path = new Path();  
  135.     path.reset();  
  136.     path.moveTo(x1, y1);  
  137.     path.quadTo(anchorX1, anchorY1, x2, y2);  
  138.     path.lineTo(x4, y4);  
  139.     path.quadTo(anchorX2, anchorY2, x3, y3);  
  140.     path.lineTo(x1, y1);  
  141.     return path;  
  142. }  

   

 再来看仿QQ聊天列表的粘连效果,我们已经实现了粘连体的绘制,接下来,我们需要实现以上的基本效果,我们给控件设置一个粘连的最大距离,即如果两个圆之间的距离超过这个值,则不再绘制粘连体。

 好了,我们看效果图:

 

        

粘连体除了在类似QQ上这种效果,其实还可以做很多事,比如,如果我们用它来实现一个加载页面的效果呢。




 


 

(3)三阶贝塞尔曲线——弹性球

三阶贝塞尔曲线,就是有两个控制点,公式太复杂,我也不是很理解,不过通过之前给出的那个网址来理解,还是比较好明白的,它相比二阶曲线的优点是,由于控制点的增加,它能够更加轻松地绘制出更平滑更自然的曲线。

先来看一个web前端的效果:



真的是很酷炫......

如何绘制类似这种,看起来具有弹性球的滑动球,我们需要使用三阶贝塞尔曲线,那么首先如何用三阶贝塞尔曲线绘制出一个圆,这里有一篇文章,是关于如何用贝塞尔曲线绘制圆:http://spencermortensen.com/articles/bezier-circle/ ,大概意思是讲,我们需要一个值就是c = 0.552284749,如下图,要绘制右上角的圆弧,我们需要两个控制点,其中B就是一个控制点,我们需要保证AB = c *r,即可以画出1/4的圆弧,以此类推,连续画四段这样的圆弧,就可以画出一个标准的圆。




接下来我们观察弹性球的运动,大概可以分为以下几个阶段:

 

 1)开始启动,此时右边点位移,其他点不动



        

2)开始加速



3)减速



4)到达终点



5)回弹效果



弹性球代码:

  1.  package com.zero.bezier.widget.elastic;  
  2.   
  3. import android.animation.Animator;  
  4. import android.animation.ValueAnimator;  
  5. import android.graphics.Path;  
  6. import android.graphics.PointF;  
  7. import android.view.animation.AccelerateDecelerateInterpolator;  
  8.   
  9. /** 
  10.  * 弹性球 
  11.  * @author linzewu 
  12.  * @date 2016/6/1 
  13.  */  
  14. public class ElasticBall extends Ball {  
  15.     /** 
  16.      * 向上运动 
  17.      */  
  18.     private static final int DIRECTION_UP = 1;  
  19.     /** 
  20.      * 向下运动 
  21.      */  
  22.     private static final int DIRECTION_DOWN = 2;  
  23.     /** 
  24.      * 向左运动 
  25.      */  
  26.     private static final int DIRECTION_LEFT = 3;  
  27.     /** 
  28.      * 向右运动 
  29.      */  
  30.     private static final int DIRECTION_RIGHT = 4;  
  31.     /** 
  32.      * 运动方向 
  33.      */  
  34.     private int mDirection;  
  35.     /** 
  36.      * 动画完成百分比(0~1) 
  37.      */  
  38.     private float mAnimPercent;  
  39.     /** 
  40.      * 弹性距离 
  41.      */  
  42.     private float mElasticDistance;  
  43.     /** 
  44.      * 弹性比例 
  45.      */  
  46.     private float mElasticPercent = 0.8f;  
  47.     /** 
  48.      * 位移距离 
  49.      */  
  50.     private float mMoveDistance;  
  51.     /** 
  52.      * 动画消费时间 
  53.      */  
  54.     private long mDuration = 1500;  
  55.       
  56.     /** 
  57.      * 偏移值 
  58.      */  
  59.     private float offsetTop, offsetBottom, offsetLeft, offsetRight;  
  60.     /** 
  61.      * 圆形偏移比例 
  62.      */  
  63.     private float c = 0.551915024494f;  
  64.       
  65.     private float c2 = 0.65f;  
  66.     /** 
  67.      * 动画开始点 
  68.      */  
  69.     private Ball mStartPoint;  
  70.   
  71.     /** 
  72.      * 动画结束点 
  73.      */  
  74.     private Ball mEndPoint;  
  75.       
  76.     /** 
  77.      * 构造方法 
  78.      * 
  79.      * @param x 圆心横坐标 
  80.      * @param y 圆心纵坐标 
  81.      * @param radius 圆半径 
  82.      */  
  83.     public ElasticBall(float x, float y, float radius) {  
  84.         super(x, y, radius);  
  85.         init();  
  86.     }  
  87.       
  88.       
  89.     private void init() {  
  90.         mElasticDistance = mElasticPercent * radius;  
  91.         offsetTop = c * radius;  
  92.         offsetBottom = c * radius;  
  93.         offsetLeft = c * radius;  
  94.         offsetRight = c * radius;  
  95.     }  
  96.       
  97.     public interface ElasticBallInterface{  
  98.         void onChange(Path path);  
  99.         void onFinish();  
  100.     }  
  101.   
  102.     private ElasticBallInterface mElasticBallInterface;  
  103.   
  104.     /** 
  105.      * 对外公布方法,设置弹性比例 (0~1) 
  106.      * @param elasticPercent 
  107.      */  
  108.     public void setElasticPercent(float elasticPercent) {  
  109.           
  110.     }  
  111.     /** 
  112.      * 对外公布方法,设置动画时间 
  113.      * @param duration 
  114.      */  
  115.     public void setDuration(long duration) {  
  116.         this.mDuration = duration;  
  117.     }  
  118.       
  119.     /** 
  120.      * 对外公布方法, 开启动画 
  121.      * @param endPoint 
  122.      */  
  123.     public void startElasticAnim(PointF endPoint, ElasticBallInterface elasticBallInterface) {  
  124.         this.mEndPoint = new ElasticBall(endPoint.x, endPoint.y, radius);  
  125.         this.mStartPoint = new ElasticBall(x, y, radius);  
  126.         this.mStatusPoint1 = new ElasticBall(x, y, radius);  
  127.         this.mStatusPoint2 = new ElasticBall(x, y, radius);  
  128.         this.mStatusPoint3 = new ElasticBall(x, y, radius);  
  129.         this.mStatusPoint4 = new ElasticBall(x, y, radius);  
  130.         this.mStatusPoint5 = new ElasticBall(x, y, radius);  
  131.         this.mElasticBallInterface = elasticBallInterface;  
  132.         judgeDirection();  
  133.         mMoveDistance = getDistance(mStartPoint.x, mStatusPoint1.y, endPoint.x, endPoint.y);  
  134.         animStatus0();  
  135.         ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);  
  136.         valueAnimator.setDuration(mDuration);  
  137.         valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());  
  138.         valueAnimator.start();  
  139.         valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  140.             @Override  
  141.             public void onAnimationUpdate(ValueAnimator animation) {  
  142.                 mAnimPercent = (float) animation.getAnimatedValue();  
  143.                 if(mAnimPercent>=0 && mAnimPercent <= 0.2){  
  144.                     animStatus1();  
  145.                 }  
  146.                 else if(mAnimPercent > 0.2 && mAnimPercent <= 0.5){  
  147.                     animStatus2();  
  148.                 }  
  149.                 else if(mAnimPercent > 0.5 && mAnimPercent <= 0.8){  
  150.                     animStatus3();  
  151.                 }  
  152.                 else if(mAnimPercent > 0.8 && mAnimPercent <= 0.9){  
  153.                     animStatus4();  
  154.                 }  
  155.                 else if(mAnimPercent > 0.9&&mAnimPercent <= 1){  
  156.                     animStatus5();  
  157.                 }  
  158.                 if (mElasticBallInterface != null) {  
  159.                     mElasticBallInterface.onChange(drawElasticCircle(topX, topY, offsetTop, offsetTop,  
  160.                             bottomX, bottomY, offsetBottom, offsetBottom,  
  161.                             leftX, leftY, offsetLeft, offsetLeft,  
  162.                             rightX, rightY, offsetRight, offsetRight));  
  163.                 }  
  164.             }  
  165.         });  
  166.         valueAnimator.addListener(new Animator.AnimatorListener() {  
  167.             @Override  
  168.             public void onAnimationStart(Animator animation) {  
  169.                   
  170.             }  
  171.   
  172.             @Override  
  173.             public void onAnimationEnd(Animator animation) {  
  174.                 if (mElasticBallInterface != null) {  
  175.                     mElasticBallInterface.onFinish();  
  176.                 }  
  177.             }  
  178.   
  179.             @Override  
  180.             public void onAnimationCancel(Animator animation) {  
  181.   
  182.             }  
  183.   
  184.             @Override  
  185.             public void onAnimationRepeat(Animator animation) {  
  186.   
  187.             }  
  188.         });  
  189.     }  
  190.   
  191.     private void judgeDirection() {  
  192.         if (mEndPoint.x - mStartPoint.x > 0) {  
  193.             mDirection = DIRECTION_RIGHT;  
  194.         }else if (mEndPoint.x - mStartPoint.x < 0) {  
  195.             mDirection = DIRECTION_LEFT;  
  196.         }else if (mEndPoint.y - mStartPoint.x > 0) {  
  197.             mDirection = DIRECTION_DOWN;  
  198.         }else if (mEndPoint.y - mStartPoint.y < 0){  
  199.             mDirection = DIRECTION_UP;  
  200.         }  
  201.     }  
  202.       
  203.     /** 
  204.      * 动画状态0 (初始状态:圆形) 
  205.      */  
  206.     private void animStatus0() {  
  207.         offsetTop = c * radius;  
  208.         offsetBottom = c * radius;  
  209.         offsetLeft = c * radius;  
  210.         offsetRight = c * radius;  
  211.     }  
  212.       
  213.     private Ball mStatusPoint1;  
  214.       
  215.     /** 
  216.      * 动画状态1 (0~0.2) 
  217.      */  
  218.     private void animStatus1() {  
  219.         float percent = mAnimPercent * 5f;  
  220.         if (mDirection == DIRECTION_LEFT) {  
  221.             leftX = mStartPoint.leftX - percent * mElasticDistance;  
  222.         } else if (mDirection == DIRECTION_RIGHT) {  
  223.             rightX = mStartPoint.rightX + percent * mElasticDistance;  
  224.         } else if (mDirection == DIRECTION_UP) {  
  225.             topY = mStartPoint.topY - percent * mElasticDistance;  
  226.         } else if (mDirection == DIRECTION_DOWN) {  
  227.             bottomY = mStartPoint.bottomY + percent * mElasticDistance;  
  228.         }  
  229.         mStatusPoint1.refresh(x, y, topX, topY, bottomX, bottomY,  
  230.                 leftX, leftY, rightX, rightY);  
  231.     }  
  232.   
  233.     private Ball mStatusPoint2;  
  234.       
  235.     /** 
  236.      * 动画状态2 (0.2~0.5) 
  237.      */  
  238.     private void animStatus2() {  
  239.         float percent = (float) ((mAnimPercent - 0.2) * (10f / 3));  
  240.         if (mDirection == DIRECTION_LEFT) {  
  241.             leftX = mStatusPoint1.leftX - percent * (mMoveDistance / 2 - mElasticDistance / 2 );  
  242.             x = mStatusPoint1.x - percent * (mMoveDistance / 2);  
  243.             rightX = mStatusPoint1.rightX - percent * (mMoveDistance / 2 - mElasticDistance / 2 );  
  244.             topX = x;  
  245.             bottomX = x;  
  246.             //偏移值稍作变化  
  247.             offsetTop = radius * c + radius * ( c2 - c ) * percent;  
  248.             offsetBottom = radius * c + radius * ( c2 - c ) * percent;  
  249.         } else if (mDirection == DIRECTION_RIGHT) {  
  250.             rightX = mStatusPoint1.rightX + percent * (mMoveDistance / 2 - mElasticDistance / 2 );  
  251.             x = mStatusPoint1.x + percent * (mMoveDistance / 2);  
  252.             leftX = mStatusPoint1.leftX + percent * (mMoveDistance / 2 - mElasticDistance / 2 );  
  253.             topX = x;  
  254.             bottomX = x;  
  255.             //偏移值稍作变化  
  256.             offsetTop = radius * c + radius * ( c2 - c ) * percent;  
  257.             offsetBottom = radius * c + radius * ( c2 - c ) * percent;  
  258.         } else if (mDirection == DIRECTION_UP) {  
  259.             topY = mStatusPoint1.topY - percent * (mMoveDistance / 2 - mElasticDistance / 2 );  
  260.             y = mStatusPoint1.y - percent * (mMoveDistance / 2);  
  261.             bottomY = mStatusPoint1.bottomY - percent * (mMoveDistance / 2 - mElasticDistance / 2 );  
  262.             leftY = y;  
  263.             rightY = y;  
  264.             //偏移值稍作变化  
  265.             offsetLeft = radius * c + radius * ( c2 - c ) * percent;  
  266.             offsetRight = radius * c + radius * ( c2 - c ) * percent;  
  267.         } else if (mDirection == DIRECTION_DOWN) {  
  268.             bottomY = mStatusPoint1.bottomY + percent * (mMoveDistance / 2 - mElasticDistance / 2 );  
  269.             y = mStatusPoint1.y + percent * (mMoveDistance / 2);  
  270.             topY = mStatusPoint1.topY + percent * (mMoveDistance / 2 - mElasticDistance / 2 );  
  271.             leftY = y;  
  272.             rightY = y;  
  273.             //偏移值稍作变化  
  274.             offsetLeft = radius * c + radius * ( c2 - c ) * percent;  
  275.             offsetRight = radius * c + radius * ( c2 - c ) * percent;  
  276.         }  
  277.         mStatusPoint2.refresh(x, y, topX, topY, bottomX, bottomY,  
  278.                 leftX, leftY, rightX, rightY);  
  279.     }  
  280.   
  281.     private Ball mStatusPoint3;  
  282.       
  283.     /** 
  284.      * 动画状态3 (0.5~0.8) 
  285.      */  
  286.     private void animStatus3() {  
  287.         float percent = (mAnimPercent - 0.5f) * (10f / 3f);  
  288.         if (mDirection == DIRECTION_LEFT) {  
  289.             leftX = mStatusPoint2.leftX - Math.abs(percent * (mEndPoint.rightX - mStatusPoint2  
  290.                     .rightX));  
  291.             x = mStatusPoint2.x - Math.abs(percent * (mEndPoint.x - mStatusPoint2.x));  
  292.             rightX = mStatusPoint2.rightX - Math.abs(percent * (mEndPoint.x - mStatusPoint2.x));  
  293.             topX = x;  
  294.             bottomX = x;  
  295.             //偏移值稍作变化  
  296.             offsetTop = radius * c2 - radius * ( c2 - c ) * percent;  
  297.             offsetBottom = radius * c2 - radius * ( c2 - c ) * percent;  
  298.         } else if (mDirection == DIRECTION_RIGHT) {  
  299.             rightX = mStatusPoint2.rightX + percent * (mEndPoint.rightX - mStatusPoint2.rightX);  
  300.             x = mStatusPoint2.x + percent * (mEndPoint.x - mStatusPoint2.x);  
  301.             leftX = mStatusPoint2.leftX + percent * (mEndPoint.x - mStatusPoint2.x);  
  302.             topX = x;  
  303.             bottomX = x;  
  304.             //偏移值稍作变化  
  305.             offsetTop = radius * c2 - radius * ( c2 - c ) * percent;  
  306.             offsetBottom = radius * c2 - radius * ( c2 - c ) * percent;  
  307.         } else if (mDirection == DIRECTION_UP) {  
  308.             topY = mStatusPoint2.topY - Math.abs(percent * (mEndPoint.topY - mStatusPoint2  
  309.                     .topY));  
  310.             y = mStatusPoint2.y - Math.abs(percent * (mEndPoint.y - mStatusPoint2.y));  
  311.             bottomY = mStatusPoint2.bottomY - Math.abs(percent * (mEndPoint.y - mStatusPoint2.y));  
  312.             leftY = y;  
  313.             rightY = y;  
  314.             //偏移值稍作变化  
  315.             offsetLeft = radius * c2 - radius * ( c2 - c ) * percent;  
  316.             offsetRight = radius * c2 - radius * ( c2 - c ) * percent;  
  317.         } else if (mDirection == DIRECTION_DOWN) {  
  318.             bottomY = mStatusPoint2.bottomY + percent * (mEndPoint.bottomY - mStatusPoint2  
  319.                     .bottomY);  
  320.             y = mStatusPoint2.y + percent * (mEndPoint.y - mStatusPoint2.y);  
  321.             topY = mStatusPoint2.topY + percent * (mEndPoint.y - mStatusPoint2.y);  
  322.             leftY = y;  
  323.             rightY = y;  
  324.             //偏移值稍作变化  
  325.             offsetLeft = radius * c2 - radius * ( c2 - c ) * percent;  
  326.             offsetRight = radius * c2 - radius * ( c2 - c ) * percent;  
  327.         }  
  328.         mStatusPoint3.refresh(x, y, topX, topY, bottomX, bottomY,  
  329.                 leftX, leftY, rightX, rightY);  
  330.     }  
  331.   
  332.     private Ball mStatusPoint4;  
  333.       
  334.     /** 
  335.      * 动画状态4 (0.8~0.9) 
  336.      */  
  337.     private void animStatus4() {  
  338.         float percent = (float) (mAnimPercent - 0.8) * 10;  
  339.         if (mDirection == DIRECTION_LEFT) {  
  340.             rightX = mStatusPoint3.rightX - percent * (Math.abs(mEndPoint.rightX - mStatusPoint3  
  341.                     .rightX) + mElasticDistance/2);  
  342.             //再做一次赋值,防止和终点不重合  
  343.             leftX = mEndPoint.leftX;  
  344.             x = mEndPoint.x;  
  345.             bottomX = mEndPoint.bottomX;  
  346.             topX = mEndPoint.topX;  
  347.         } else if (mDirection == DIRECTION_RIGHT) {  
  348.             leftX = mStatusPoint3.leftX + percent * (mEndPoint.leftX - mStatusPoint3.leftX +  
  349.                     mElasticDistance/2);  
  350.             //再做一次赋值,防止和终点不重合  
  351.             rightX = mEndPoint.rightX;  
  352.             x = mEndPoint.x;  
  353.             bottomX = mEndPoint.bottomX;  
  354.             topX = mEndPoint.topX;  
  355.         } else if (mDirection == DIRECTION_UP) {  
  356.             bottomY = mStatusPoint3.bottomY - percent * (Math.abs(mEndPoint.bottomY - mStatusPoint3  
  357.                     .bottomY) + mElasticDistance/2);  
  358.             //再做一次赋值,防止和终点不重合  
  359.             topY = mEndPoint.topY;  
  360.             y = mEndPoint.y;  
  361.             leftY = mEndPoint.leftY;  
  362.             rightY = mEndPoint.rightY;  
  363.         } else if (mDirection == DIRECTION_DOWN) {  
  364.             topY = mStatusPoint3.topY + percent * (mEndPoint.topY - mStatusPoint3  
  365.                     .topY + mElasticDistance/2);  
  366.             //再做一次赋值,防止和终点不重合  
  367.             bottomY = mEndPoint.bottomY;  
  368.             y = mEndPoint.y;  
  369.             leftY = mEndPoint.leftY;  
  370.             rightY = mEndPoint.rightY;  
  371.         }  
  372.         mStatusPoint4.refresh(x, y, topX, topY, bottomX, bottomY,  
  373.                 leftX, leftY, rightX, rightY);  
  374.     }  
  375.   
  376.     private Ball mStatusPoint5;  
  377.       
  378.     /** 
  379.      * 动画状态5 (0.9~1)回弹 
  380.      */  
  381.     private void animStatus5() {  
  382.         float percent = (float) (mAnimPercent - 0.9) * 10;  
  383.         if (mDirection == DIRECTION_LEFT) {  
  384.             rightX = mStatusPoint4.rightX + percent * (mEndPoint.rightX - mStatusPoint4.rightX);  
  385.         } else if (mDirection == DIRECTION_RIGHT) {  
  386.             leftX = mStatusPoint4.leftX + percent * (mEndPoint.leftX - mStatusPoint4.leftX);  
  387.         } else if (mDirection == DIRECTION_UP) {  
  388.             bottomY = mStatusPoint4.bottomY + percent * (mEndPoint.bottomY - mStatusPoint4.bottomY);  
  389.         } else if (mDirection == DIRECTION_DOWN) {  
  390.             topY = mStatusPoint4.topY + percent * (mEndPoint.topY - mStatusPoint4.topY);  
  391.         }  
  392.         mStatusPoint5.refresh(x, y, topX, topY, bottomX, bottomY,  
  393.                 leftX, leftY, rightX, rightY);  
  394.     }  
  395.   
  396.     /** 
  397.      * 绘制弹性圆 
  398.      * 通过绘制四段三阶贝塞尔曲线,来实现有弹性变化的圆 
  399.      * @param topX 
  400.      * @param topY 
  401.      * @param offsetTop1 
  402.      * @param offsetTop2 
  403.      * @param bottomX 
  404.      * @param bottomY 
  405.      * @param offsetBottom1 
  406.      * @param offsetBottom2 
  407.      * @param leftX 
  408.      * @param leftY 
  409.      * @param offsetLeft1 
  410.      * @param offsetLeft2 
  411.      * @param rightX 
  412.      * @param rightY 
  413.      * @param offsetRight1 
  414.      * @param offsetRight2 
  415.      * @return 
  416.      */  
  417.     private Path drawElasticCircle(  
  418.             float topX, float topY, float offsetTop1, float offsetTop2,  
  419.             float bottomX, float bottomY, float offsetBottom1, float offsetBottom2,  
  420.             float leftX, float leftY, float offsetLeft1, float offsetLeft2,  
  421.             float rightX, float rightY, float offsetRight1, float offsetRight2  
  422.     ) {  
  423.         /** 
  424.          * 绘制每一段三阶贝塞尔曲线需要两个控制点 
  425.          */  
  426.         PointF controlTop1, controlTop2, controlBottom1, controlBottom2,  
  427.                 controlLeft1, controlLeft2, controlRight1, controlRight2;  
  428.         controlTop1 = new PointF();  
  429.         controlTop1.x = topX - offsetTop1;  
  430.         controlTop1.y = topY;  
  431.         controlTop2 = new PointF();  
  432.         controlTop2.x = topX + offsetTop2;  
  433.         controlTop2.y = topY;  
  434.         controlBottom1 = new PointF();  
  435.         controlBottom1.x = bottomX - offsetBottom1;  
  436.         controlBottom1.y = bottomY;  
  437.         controlBottom2 = new PointF();  
  438.         controlBottom2.x = bottomX + offsetBottom2;  
  439.         controlBottom2.y = bottomY;  
  440.         controlLeft1 = new PointF();  
  441.         controlLeft1.x = leftX;  
  442.         controlLeft1.y = leftY - offsetLeft1;  
  443.         controlLeft2 = new PointF();  
  444.         controlLeft2.x = leftX;  
  445.         controlLeft2.y = leftY + offsetLeft2;  
  446.         controlRight1 = new PointF();  
  447.         controlRight1.x = rightX;  
  448.         controlRight1.y = rightY - offsetRight1;  
  449.         controlRight2 = new PointF();  
  450.         controlRight2.x = rightX;  
  451.         controlRight2.y = rightY + offsetRight2;  
  452.   
  453.         Path path = new Path();  
  454.         /** 
  455.          * 绘制top到left的圆弧 
  456.          */  
  457.         path.moveTo(topX, topY);  
  458.         path.cubicTo(controlTop1.x, controlTop1.y, controlLeft1.x, controlLeft1.y, leftX, leftY);  
  459.         /** 
  460.          * 绘制left到bottom的圆弧 
  461.          */  
  462.         path.cubicTo(controlLeft2.x ,controlLeft2.y, controlBottom1.x, controlBottom1.y, bottomX,  
  463.                 bottomY);  
  464.         /** 
  465.          * 绘制bottom到right的圆弧 
  466.          */  
  467.         path.cubicTo(controlBottom2.x, controlBottom2.y, controlRight2.x, controlRight2.y,  
  468.                 rightX, rightY);  
  469.         /** 
  470.          * 绘制right到top的圆弧 
  471.          */  
  472.         path.cubicTo(controlRight1.x, controlRight1.y, controlTop2.x, controlTop2.y, topX, topY);  
  473.         return path;  
  474.     }  
  475.   
  476.     /** 
  477.      * 求两点之间的距离 
  478.      * @param x1 第一个点的横坐标 
  479.      * @param y1 第一个点的纵坐标 
  480.      * @param x2 第二个点的横坐标 
  481.      * @param y2 第二个点的纵坐标 
  482.      * @return 两点距离 
  483.      */  
  484.     private float getDistance(float x1, float y1, float x2, float y2) {  
  485.         return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));  
  486.     }  
  487.       
  488. }  

上面完成了一个弹性球的封装,可以实现四个方向的运动,然后我们实现一个弹性球的loader:





贝塞尔曲线还有很多应用的地方,或者说在各个领域都有。

去年开始在黄同学的影响下,慢慢地去实现一些利用贝塞尔曲线实现的效果,源码有相当一部分代码也是来自于黄同学,非常感谢黄,也得益于网络上大多数技术博客无私的分享,希望自己能够通过学习这样一个开发的绘图曲线,有所提高。

贝塞尔曲线在Android中的应用        今天要讲解的内容是Android中贝塞尔曲线的应用。可能很多人对贝塞尔曲线不甚了解,这里先对它的概念做一下简单介绍。        贝塞尔曲线由多个点组成:起始点、终止点以及0到n个相互分离的中间点。根据中间点的不同,可以分为线性贝塞尔曲线、二阶贝塞尔曲线、三阶贝塞尔曲线和高阶贝塞尔曲线。一般的矢量图形软件通过它来精确画出曲线,贝塞尔曲线由线段与节点组成,节点是可拖动的支点,线段像... 阅读详情

相关推荐

Android 贝塞尔曲线

Android 贝塞尔曲线一阶贝塞尔曲线二阶贝塞尔曲线三阶贝塞尔曲线: 今天给大家介绍的是贝塞尔曲线,什么是贝塞尔曲线呢?就是一个叫贝塞尔的人,发现了这个东西,然后就以他的名字命名为贝塞尔曲线,就像司马光砸缸一样,人家为啥要叫司马光砸缸,不叫小明砸缸小马砸缸呢?明明是司马光砸的刚功劳不能被别人抢走呀,好了好了这100多字都是废话,因为我在别的博客看的太多了,粘贴过来是真没意思。直接看效果上代码给大家介绍喽。 效果图(1.1): 咱们先来看一阶贝塞尔曲线: 一阶贝塞尔曲线 @Override

高级开发者进阶ing... QaQ 2518

Android 贝塞尔曲线解析

相信很多同学都知道“贝塞尔曲线”这个词,我们在很多地方都能经常看到。利用“贝塞尔曲线”可以做出很多好看的UI效果,本篇博客就让我们一起学习“贝塞尔曲线”。贝塞尔曲线的原理贝塞尔曲线是用一系列点来控制曲线状态的,这些点简单分为两类: 类型 作用 数据点 确定曲线的起始和结束位置 控制点 确定曲线的弯曲程度 一阶贝塞尔曲线 一阶曲线是没有控制点的,仅有两个数据点(A 和 B),最

一个码农的博客 1万+

安卓自定义View进阶 - 贝塞尔曲线

在上一篇文章Path之基本图形中我们了解了Path的基本使用方法,本次了解Path中非常非常非常重要的内容-贝塞尔曲线。

GcsSloop 1万+

Android 贝塞尔曲线的实现(一)

什么是贝塞尔曲线 简单的来说贝塞尔曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,带来视觉上的冲击,我们可以利用它来画出各种意想不到的曲线, 这里就不在列出贝塞尔曲线的繁琐定义及推导了,感兴趣的小伙伴可自行百度。 贝塞尔曲线分为一阶贝塞尔曲线,二阶贝塞尔曲线,三阶贝塞尔曲线甚至多阶。 对于Android开发十分友好,已经为我们封装好了简单的api供使用。 AndroidApi 二阶贝...

WormholeStack 7680

Android 贝塞尔曲线的魅力

前几天在手机优快云客户端看到一个QQ红点拖拽消失的效果,感觉挺好的,又回想到以前见到的诸如一下效果,感觉挺炫酷的,于是就看了下QQ拖拽的代码,记下笔记。 , 这个是在泡网上找到 还有一个指示器的效果,那个一时半会找不见了。UC浏览器新闻那一块的下拉刷新也是用了贝塞尔曲线的。什么是贝塞尔曲线 在数学的数值分析领域中,贝塞尔曲线, 又称贝赛尔曲线(Bézier曲线)是电脑图形学中

_StriveG的专栏 8759

YOLO11实战:手把手教你优化钢材表面缺陷识别模型(基于NEU-DET数据集)

本文详细介绍了如何使用YOLO11优化钢材表面缺陷识别模型,基于NEU-DET数据集进行实战训练。从数据增强、模型架构优化到训练过程控制,全面解析提升钢材缺陷检测性能的关键技术,特别针对裂纹、划痕等典型缺陷提出针对性解决方案。

weixin_29081163的博客 379

贝塞尔曲线----原理以及在Android平台的实现

一、什么是贝塞尔曲线? 贝塞尔曲线于 1962 年,由法国工程师皮埃尔·贝济埃(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。 贝塞尔曲线主要用于二维图形应用程序中的数学曲线,曲线由起始点,终止点(也称锚点)和控制点组成,通过调整控制点,通过一定方式绘制的贝塞尔曲线形状会发生变化。后面会具体介绍绘制的方法。 在计算机图形学中贝赛尔曲线的运用很广泛,例如Pho...

Groople的专栏 2024

VBA中如何使用Edge内核Browser?

在微软宣布停止对IE的支持后,许多使用VBA中Web Browser的用户遇到了困境,因为Web Browser使用的IE内核。然而微软并没有给出支持Edge的新版Web Browser,网上许多第三方的库也不够完善。经过调研,libEdge可以较好的和VBA集成,实现在Excel中打开Browser,甚至能够通过执行js代码,获取web页面的信息。项目需要WebView2Loader.dll和LibEdge.dll两个库,区分x86和x64版本。

金木AI 2396

android贝塞尔曲线,一文解析 Android 贝塞尔曲线

原标题:一文解析 Android 贝塞尔曲线相信很多同学都知道“贝塞尔曲线”这个词,我们在很多地方都能经常看到。利用“贝塞尔曲线”可以做出很多好看的UI效果,本篇博客就让我们一起学习“贝塞尔曲线”。1贝塞尔曲线的原理贝塞尔曲线是用一系列点来控制曲线状态的,这些点简单分为两类:类型作用数据点确定曲线的起始和结束位置控制点确定曲线的弯曲程度一阶贝塞尔曲线一阶曲线是没有控制点的,仅有两个数据点(A 和 ...

weixin_39975683的博客 510

【雷达仿真 | FMCW TDMA-MIMO毫米波雷达信号处理仿真(可修改为DDMA-MIMO)】

1、FMCW叫做调频连续波,调频连续波有步进调频连续波(SFMCW)、线性调频连续波(LFMCW),以及其他非线性调频连续波。LFMCW有锯齿波和三角波等等;锯齿波有恒定调制周期的锯齿波和非恒定周期的锯齿波。Introduction to mmwave sensing:FMCW radars.pdf和The fundamentals of millimeter wave sensors。2、MIMO是多输入多输出(Multiple input Multiple output),也就是多发多收。

【调皮连续波】雷达技术领域知识博主 1万+

【StreamSet】ETL之StreamSet之Pipelines的状态监听之WebHook钉钉篇

介绍下StreamSets的状态监听以及钉钉的webhook的互联,这位博友问的恰是时候,不久前我刚刚对接过钉钉的机器人,这次看到这个问题,对我来说,应该轻车熟路了,那就有了这篇博文,状态监听之钉钉篇。...

m0_54252387的博客 487

android 三阶曲线动画,安卓动画插值器之三次贝塞尔曲线

8种机械键盘轴体对比本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?文章目录安卓平台实现动画效果的时候,往往为了需要实现一个平滑的,酷的效果去自定义一个动画插值器,即Interpolator。下面介绍一种可视化的方法创建一个基于三次贝塞尔曲线的插值器。关于动画插值器关于安卓动画插值器,即Interpolator。实际上即是一个横轴为时间,纵轴为变化量的函数曲线。动画的快慢按照曲线的斜率去...

weixin_36277530的博客 504

基于Simulink的PMSM轮毂电机崎岖路面悬架协同控制仿真

本文完成了基于Simulink的PMSM轮毂电机崎岖路面悬架协同控制仿真,实现了:✅ 构建了四轮独立驱动+主动悬架高保真整车模型✅ 实现了Skyhook主动悬架控制✅ 设计了驱动-悬架状态共享与协同决策机制✅ 验证了5 m/s高速下连续颠簸路面稳定通过能力✅ 为高性能移动机器人提供一体化底盘解决方案。

xiaoheshang_123的博客 190

android贝塞尔曲线动画,基于贝塞尔曲线的Android动画差值器

一、简介贝塞尔曲线于1962,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由Paul de Casteljau于1959年运用de Casteljau算法开发,以稳定数值的方法求出贝兹曲线。贝塞尔曲线不仅仅可以应用到工业设计中,在计算机动画开发中同样占有一席之地,通过构造贝塞尔曲线模拟物体运动的轨迹、速度甚至加速度,来达...

weixin_42314737的博客 1153

【法如faro】三维激光软件Scene2023数据处理(自动配准并转换坐标)流程

法如faro Scene2023数据处理(自动配准并转换坐标)的主要流程为:新建项目、导入数据、处理、自动注册、坐标系转换、模型导出立和面模型导出等。

智慧空间实验室 2382

9、储备池学习:从学习到学习的新范式

本文探讨了基于学习到学习(L2L)框架的储备池计算新范式,提出通过在外循环中优化储备池的超参数(如递归连接权重、输入权重和读出初始化),以提升其在多种任务中的内循环学习性能。研究展示了两种主要实验设置:一是优化储备池用于回归二阶Volterra滤波器任务,显著优于随机基线;二是实现无需调整读出权重、仅依赖内部动态学习非线性函数的能力,表明储备池可快速构建目标函数的内部模型并安装任务先验知识。结合流程图与表格,文章系统呈现了L2L的训练流程、技术细节及实验结果,并讨论了性能提升、泛化能力等开放问题,指出了未来

ujm56789012的博客 502

Android初识贝塞尔曲线,安卓移动开发实验报告

private Paint getPaintByColor(int color) { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setDither(true); paint.setColor(color); paint.setStyle(Paint.Style.STROKE); return paint; } @Override protected void onMeasure(int widthMeasureSpec, int h

m0_65145113的博客 180

机器人中的数值优化(二十一)—— 伴随灵敏度分析、线性方程组求解器的分类和特点、优化软件

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考,主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等,本系列文章篇数较多,不定期更新,上半部分介绍无约束优化,下半部分介绍带约束的优化,中间会穿插一些路径规划方面的应用实例

慕羽★的博客 4205

Android贝塞尔曲线实现Loading效果

在自己开发项目过程中,经常会用到加载框,如:网络请求等待等,这时我们则需要一段时间的等待以保证数据加载完毕,此时加一个等待框则显得非常具有必要性,以提高用户体验性。本文将通过GABottleLoading插件实现水滴飞溅的加载效果,贝塞尔曲线的又一次运用。 一、贝塞尔曲线     贝塞尔曲线,又称贝兹曲线或贝济埃曲线,主要应用于二维图形应用程序的数学曲线。贝塞尔曲线是计算机图形图像造型的基本工

夏天的夏,夏天的天。 6527

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Android 贝塞尔曲线原理及其绘制实现

Android动画,特别是涉及运动轨迹的,很多时候都会使用贝塞尔曲线,下面将系统的介绍下贝塞尔曲线的相关知识。 什么是贝塞尔曲线 贝塞尔曲线于1962年,由法国工程师皮埃尔·贝兹(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由保尔·德·卡斯特里奥于1959年运用德卡斯特里奥算法开发,以稳定数值的方法求出贝塞尔曲线。 下文我们将简要介绍德卡斯特里奥算...

S.H.的部落格林 1772

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

安卓平滑曲线的实现:三次贝塞尔曲线

参考链接 https://www.jianshu.com/p/c4601bab860a 理论知识: 坐标计算 令 A0和B3连线的斜率 k = (B3Y - A0Y) / (B3X - A0X) 常数 b = A3Y - k * A3X 则 A2的X坐标 A2X = A3X - (A3X - A0X) * rate A2的Y坐标 A2Y = k * A2X + b B1的X坐标 B1X = A3X...

weixin_38282516的博客 1171

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Android开发 之 曲线运动动画(贝塞尔曲线)

曲线运动动画(贝塞尔曲线) 贝塞尔曲线:维基百科中这样说到:在数学的数值分析领域中,贝塞尔曲线(英语:Bézier curve)是计算机图形学中相当重要的参数曲线。更高维度的广泛化贝塞尔曲线就称作贝塞尔曲面,其中贝塞尔三角是一种特殊的实例。贝塞尔曲线于1962年,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初

z_x_Qiang的博客 6494

Android初识贝塞尔曲线

/如果你发现你的 onTouchEvent 无法捕获 MOVE 事件,就是因为这个方法返回的是 false导致的。//绘制二阶贝塞尔曲线。

2401_87256382的博客 973

安卓开发之实现控件的贝塞尔曲线路径动画

贝塞尔曲线一般用在绘制canvas图层上,这里主要是ObjectAnimation动画及插值器和估值器实现将贝塞尔曲线作为控件移动的动画路径。 项目demo图:一、ObjectAnimator动画原理 ObjectAnimator.ofObject(….,”xxx”,估值值,区间数组); 【定义动画属性xxx和区间】 插值器/加速器(Interpolator)【返回当前数字进度t】 估值值(Eval

cxmscb的博客 9149
上一篇: Android实现小球一直运动
下一篇: Android 贝塞尔曲线的魅力
Happy_Develop_
博客等级 码龄12年 26粉丝 99原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值