贝塞尔曲线的入门

贝塞尔曲线的神秘之处就是能实现非常酷炫的动画效果,,对此还需先了解它的原理.先来看下贝塞尔曲线是什么样子的。

贝塞尔曲线分为几种阶段的曲线,一般我们常用到的是二阶,三阶。四阶也有。更高阶的比较少见到,而且android提供给我们的方法也就到三阶的方法。四阶以上需要自己写算法去计算。他们的实现都有分别不同的公式实现,先上图。
一阶是一条直线,这没什么好说的。
公式:


一阶.png

二阶贝塞尔曲线:


二阶.png

1.gif

三阶贝塞尔曲线:


三阶.png
2.gif

四阶贝塞尔曲线:


4.gif

以上就是常见的贝塞尔曲线的曲线样子。
虽然有这些公式,但是具体的绘制原理是什么呢。
这些公式的里有一个t的参数。这个t表示的是时间值。取值为0到1。先从简单的二阶曲线讲起吧。


sdfsd.png

可以看到,po是起点,p2是终点,但是这个p1点就起到了关键作用。p1可以说成是控制点。我们假设知道三个点的坐标已知。在某个t时刻,将这个t值代入到公式里。就可以算出在这个当前时刻的点坐标。所以这个t值,如果从0到1全部算出得到所有的点坐标,连起来也就是我们所看到的二阶曲线。那图中的绿色线的两个点又表示的是什么意思呢。
这个t值在这里也可以理解为比例值。从p0到p1这条线的36%的位置标记点。从p1到p2这条线的36%的位置标记点。这个两个点连起来的线,再在它的36%的位置标记点。这个最终算点的位置也是就是t位置0.36时刻的坐标点。所以同样在3阶,4阶,以及n阶的曲线,都是按照这样的原理去绘制点。
原理都懂了。先看看代码是如何实现的。我们先自定义一个类继承View,实现构造方法,onDraw方法。需要画笔和画线的类。

"public class Test3Line extends View {

  public Test3Line(Context context) {
      super(context);
      initView();
   }

  public Test3Line(Context context, AttributeSet attrs) {
       super(context, attrs);
       initView();
  }

   public Test3Line(Context context, AttributeSet attrs, int defStyleAttr) {
       super(context, attrs, defStyleAttr);
       initView();
   }

   @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
   public Test3Line(Context context, AttributeSet attrs, int defStyleAttr,    int defStyleRes) {
       super(context, attrs, defStyleAttr, defStyleRes);
       initView();
   }

   private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   private final Path mPath = new Path();

   private void initView(){
       Paint paint = mPaint;
       //抗锯齿
       paint.setAntiAlias(true);
       //抗抖动
       paint.setDither(true);
       //线的粗细
       paint.setStyle(Paint.Style.STROKE);
       paint.setStrokeWidth(5);

       //一阶
       Path path = mPath;
       path.moveTo(100,100);
       path.lineTo(400,400);

       //二阶的方法
       path.quadTo(600,100,800,400);//前两个参数是控制点xy的高度,后面两个是最终点的位置。
       //相对的实现  灵活。随机应变。增加多少。
       // path.rQuadTo(200,-300,400,0);

        path.moveTo(400,800);
       //  path.rQuadTo(200,-300,400,0);

       //三阶的方法。
       //  path.cubicTo(500,600,700,1200,800,800);
          path.rCubicTo(100,-200,300,400,400,0);
     }

     @Override
     protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(mPath,mPaint);
     }
    }

上面二阶的有了quadTo方法,为啥还有个rQuadTo方法,这个方法就像是相对布局一样。相对于前一个坐标点,加入你想要的增加的值就能算出quadTo方法一样的效果。
同样,三阶的rCubicTo()方法也是同样的道理。
最后绘制出的图

213123.png

四阶以及N阶之后,android就没有提供相应的方法了。下面就给出相应的算法吧

 public class BezierView extends View {
    public BezierView(Context context) {
        super(context);
        init();
   }

    public BezierView(Context context, AttributeSet attrs) {
       super(context, attrs);
       init();
   }

    public BezierView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private Path mBezirer = new Path();
    private Path mSrcBezirer = new Path();
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public BezierView(Context context, AttributeSet attrs, int defStyleAttr, int   defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init(){
        //初始化画笔
        Paint paint = mPaint;
        paint.setAntiAlias(true);
        //抗抖动
        paint.setDither(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);

        //初始化贝塞尔曲线4阶
        //initBezier();

        //初始化原线
        mSrcBezirer.cubicTo(200,700,500,1200,700,200);

        new Thread(){//开启线程,延迟绘制出曲线
            @Override
            public void run(){
                initBezier();
            }
        }.start();
    }

    /**
     * 初始化贝塞尔曲线
     */
    private void initBezier(){
        //6阶曲线
        //数组中,第一个和最后一个为起始点的x或者y坐标,中间的就是控制点的x或y坐标
        float[] xPoints= new float[]{0,200,500,700,400,300,900};//5个控制点的x坐标 
        float[] yPoints = new float[]{0,700,1200,200,400,100,1000};//5个控制点的y坐标
        //float progress = 0.2f;// ...
        //calculateBezier(progress,xPoints);

        Path path = mBezirer;
    
       int fps = 1000;//刷新频率
       for(int i = 0;i <= fps;i++){
            //进度
            float progress = i/(float)fps;// ...
            float x = calculateBezier(progress,xPoints);
            float y = calculateBezier(progress,yPoints);
            //使用链接的方式,当xy变动足够小的情况下,就是平滑曲线.
            path.lineTo(x,y);

        //刷新界面
            postInvalidate();
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

   /**
     * 计算某时刻的贝塞尔所处的值 (x或y)
     * @param t 时间(0~1)
     * @param values 贝塞尔点集合 (x或y)
     * @return 当前t时刻 的贝塞尔所处点
     */
    private float calculateBezier(float t,float... values){
        //循环计算
        //采用双层for循环
        final int len = values.length;
        for(int i = len-1;i>0;i--){
            //外层
            for(int j = 0;j<i;j++){
                //计算
                values[j] = values[j] + (values[j+1]-values[j])*t;

            }
        }

        //运算时结果保存在第一位
        //所以返回第一位
        return values[0];
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(Color.BLACK);
        canvas.drawPath(mSrcBezirer,mPaint);
        mPaint.setColor(Color.RED);
        canvas.drawPath(mBezirer,mPaint);
    }
}

6阶曲线最终效果

6666.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容