android中贝塞尔曲线的应用示例

android中贝塞尔曲线的应用示例,第1张

概述前言:贝塞尔曲线又称贝兹曲线,它的主要意义在于无论是直线或曲线都能在数学上予以描述。最初由保罗・德卡斯特里奥(PauldeCasteljau)于1959年运用德卡斯特里奥演算法开发(deCasteljauAlgorithm),在1962,由法

前言:

贝塞尔曲线又称贝兹曲线,它的主要意义在于无论是直线或曲线都能在数学上予以描述。最初由保罗・德卡斯特里奥(Paul de Casteljau)于1959年运用德卡斯特里奥演算法开发(de Casteljau Algorithm),在1962,由法国工程师皮埃尔・贝塞尔(PIErre BézIEr)所广泛发表。目前广泛应用于图形绘制领域来模拟光滑曲线,为计算机矢量图形学奠定了基础。在一些图形处理软件中都能见到贝塞尔曲线,比如CorelDraw中翻译成“贝赛尔工具”;而在Fireworks中叫“画笔”;Photoshop中叫“钢笔工具”。下图为Photoshop中用钢笔绘制的贝塞尔曲线,共绘制了三条贝塞尔曲线:


数学表达

术语:数据点、控制线、控制点、德卡斯特里奥算法、一阶,二阶,三阶,n阶……

数据点:一条贝塞尔曲线的起始点和终结点都叫数据点。 控制线:在图中可以看到那条中心点为数据点的线段,每个数据点对应一条控制线 控制点:就是控制线的端点,通过控制线随着控制点的变化而变化;数据点和控制点决定一条贝塞尔曲线。 一阶贝塞尔曲线:其实是一条直线段,没有控制点。

一阶贝塞尔曲线示意图


一阶贝塞尔曲线公式


二阶贝塞尔曲线:图中第二段为二阶贝塞尔曲线,只有一个控制点,即只有一个控制点和两个数据点来决定曲线形状。

二阶贝塞尔曲线公式

二阶公式推导:


二阶贝塞尔曲线t=0.6示意图.gif
根据控制点和数据点,对贝塞尔曲线进行约束,满足的条件为


问题变为:已知P0(x0,y0),P1(x1,y1),P2(x2,y2),根据上式求P点坐标?

先求出A、B点坐标,其坐标

PA=P0+(P1-P0)・t

PB=P1+(P2-P1)・t

之后求P点坐标,其坐标

P=PA+(PB-PA)・t

P=(1-t)2P0+2t(1-t)P1+t2P2, t∈[0,1]

三阶贝塞尔曲线:图中第三段为三阶贝塞尔曲线,有两个控制点和两个数据点决定的曲线,同样满足等比条件:

三阶贝塞尔曲线

三阶贝塞尔曲线公式


德卡斯特里奥算法的思想:给定数据点和控制点P0、P1…Pn,首先将数据点和控制点连接形成一条折线,计算出每条折线上面的一点,使得初始数据点(初始控制点)到该点的距离与初始数据点(初始控制点)到终止数据点(终止控制点)的距离之比为t:1。将这些点连接起来形成新的折线(折线少了一段),用递归的算法继续计算,指导只有两个点,在这两个点形成的线段上去一点,满足以上的比例关系。随着t的从0到1的变化,该点的集合形成了贝塞尔曲线。

n阶贝塞尔曲线公式如下。


AndroID中的应用

对androID中如何获取贝塞尔曲线上的点,如何绘制贝塞尔曲线,以及结合贝塞尔曲线的知识做出的效果进行分析。

1. 获取贝塞尔曲线上点的坐标

在androID中并没有直接获取的方法,因此需要利用上面的公式进行计算,以二阶贝塞尔曲线为例,获取51个点,主要是t从0开始到1间取51项的等差数列:

public class MainActivity extends AppCompatActivity {  private static final float mPointNum = 50f;  @OverrIDe  protected voID onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentVIEw(R.layout.activity_main);    init();  }  private voID init() {    PointF mStartPoint = new PointF(0,0);    PointF mEndPoint = new PointF(0,1200);    PointF mControlPoint = new PointF(500,600);    List<PointF> mPointList = new ArrayList<>();    for (int i = 0; i <= mPointNum; i++) {      mPointList.add(getBezIErPoint(mStartPoint,mEndPoint,mControlPoint,i / mPointNum));      Log.d("BezIEr","X:" + mPointList.get(i).x + " Y:" + mPointList.get(i).y);    }  }  private PointF getBezIErPoint(PointF start,PointF end,PointF control,float t) {    PointF bezIErPoint = new PointF();    bezIErPoint.x = (1 - t) * (1 - t) * start.x + 2 * t * (1 - t) * control.x + t * t * end.x;    bezIErPoint.y = (1 - t) * (1 - t) * start.y + 2 * t * (1 - t) * control.y + t * t * end.y;    return bezIErPoint;  }}

如果需要更高阶,可以使用递归函数来运算

//用递归获取贝塞尔曲线点的x轴坐标private float getBezIErPointX(int n,int position,float t) {    if (n == 1) {      return (1 - t) * mPointList.get(position).x + t * mPointList.get(position + 1).x;    }    return (1 - t) * getBezIErPointX(n - 1,position,t) + t * getBezIErPointX(n - 1,position + 1,t);  }
//用递归获取贝塞尔曲线点的x轴坐标private float getBezIErPointX(int n,t);  }private ArrayList<PointF> buildBezIErPoints() {    ArrayList<PointF> points = new ArrayList<>();    int order = mPointList.size() - 1;    float delta = 1.0f / POINT_NUM;    for (float t = 0; t <= 1; t += delta) {      // BezIEr点集      points.add(new PointF(getBezIErPointX(order,t),getBezIErPointY(order,t)));    }    return points;  }

2. 绘制贝塞尔曲线

AndroID 中的Path类可以直接绘制一阶到三阶的贝塞尔曲线,在onDraw(Canvas canvas) 方法中使用:

绘制一阶贝塞尔曲线:

canvas.drawline(start.x,start.y,end.x,end.y);

绘制二阶贝塞尔曲线:

mPath.moveto(startPoint.x,startPoint.y);//起点mPath.quadTo(controlPoint1.x,controlPoint1.y,endPoint.x,endPoint.y);canvas.drawPath(mPath,mPaint);

绘制三阶贝塞尔曲线:

mPath.moveto(startPoint.x,startPoint.y);//起点mPath.cubicTo(controlPoint1.x,controlPoint2.x,controlPoint2.y,mPaint);

绘制n阶贝塞尔曲线

n阶贝塞尔曲线绘制,需要结合递归函数,设定一条曲线由多少个点组成,通过循环获取每个点的坐标进行绘制。

7阶贝塞尔曲线

8阶贝塞尔曲线

3.Demo

贝塞尔曲线在androID中最常用的是做出一些动画特效,如QQ消息数拖拽形变效果,一些炫酷的下拉刷新控件,阅读软件的翻书效果,一些平滑的折线图的制作,某图片的运动轨迹等……

3.1 形状变形

只需要知道变形前和变形后图形的数据点和控制点即可,通过改变数据点和控制点使得形状发生改变。


初始化

  private float[] mData = new float[8];        // 顺时针记录绘制圆形的四个数据点  private float[] mCtrl = new float[16];       // 顺时针记录绘制圆形的八个控制点  private float mDuration = 1000;           // 变化总时长  private float mCurrent = 0;             // 当前已进行时长  private float mCount = 100;             // 将时长总共划分多少份  private float mPIEce = mDuration / mCount;      // 每一份的时长

在onDraw(Canvas canvas)方法中绘制

    path.reset();    path.moveto(mData[0],mData[1]);    path.cubicTo(mCtrl[0],mCtrl[1],mCtrl[2],mCtrl[3],mData[2],mData[3]);    path.cubicTo(mCtrl[4],mCtrl[5],mCtrl[6],mCtrl[7],mData[4],mData[5]);    path.cubicTo(mCtrl[8],mCtrl[9],mCtrl[10],mCtrl[11],mData[6],mData[7]);    path.cubicTo(mCtrl[12],mCtrl[13],mCtrl[14],mCtrl[15],mData[0],mData[1]);    canvas.drawPath(path,mPaint);    mCurrent += mPIEce;    if (mCurrent < mDuration) {      mData[1] -= 120 / mCount;      mCtrl[7] += 80 / mCount;      mCtrl[9] += 80 / mCount;      mCtrl[4] -= 20 / mCount;      mCtrl[10] += 20 / mCount;      postInvalIDateDelayed((long) mPIEce);    }

3.2 漂浮的爱心

爱心的漂浮轨迹就是一条三阶贝塞尔曲线,结合属性动画中的估值器进行设置。


首先定义一个属性动画的估值器

public class BezIErEvaluator implements TypeEvaluator<PointF> { private PointF mControlP1; private PointF mControlP2; public BezIErEvaluator(PointF controlP1,PointF controlP2) {   this.mControlP1 = controlP1;   this.mControlP2 = controlP2; } @OverrIDe public PointF evaluate(float time,PointF start,PointF end) {   float timeleft = 1.0f - time;   PointF point = new PointF();   point.x = timeleft * timeleft * timeleft * (start.x) + 3 * timeleft * timeleft * time *       (mControlP1.x) + 3 * timeleft * time *       time * (mControlP2.x) + time * time * time * (end.x);   point.y = timeleft * timeleft * timeleft * (start.y) + 3 * timeleft * timeleft * time *       (mControlP1.y) + 3 * timeleft * time *       time * (mControlP2.y) + time * time * time * (end.y);   return point; }}

之后自定义一个vIEw可以生成爱心,添加透明度,缩放等动画和根据贝塞尔曲线改变其位置的属性动画。

初始化爱心图片和多个插值器等,到时随即选取

 private voID init() {    // 初始化显示的图片    drawables = new Drawable[3];    drawables[0] = getResources().getDrawable(R.drawable.red);    drawables[1] = getResources().getDrawable(R.drawable.yellow);    drawables[2] = getResources().getDrawable(R.drawable.green);    // 初始化插补器    mInterpolators = new Interpolator[4];    mInterpolators[0] = new linearInterpolator();// 线性    mInterpolators[1] = new AccelerateInterpolator();// 加速    mInterpolators[2] = new DecelerateInterpolator();// 减速    mInterpolators[3] = new AccelerateDecelerateInterpolator();// 先加速后减速    // 底部 并且 水平居中    DWIDth = drawables[0].getIntrinsicWIDth();    dHeight = drawables[0].getIntrinsicHeight();    lp = new LayoutParams(DWIDth,dHeight);    lp.addRule(CENTER_HORIZONTAL,TRUE);// 这里的TRUE 要注意 不是true    lp.addRule(AliGN_PARENT_BottOM,TRUE);  }

入场动画

private AnimatorSet getEnteranimator(final VIEw target) {    ObjectAnimator Alpha = ObjectAnimator.offloat(target,VIEw.Alpha,0.2f,1f);    ObjectAnimator scaleX = ObjectAnimator.offloat(target,VIEw.SCALE_X,1f);    ObjectAnimator scaleY = ObjectAnimator.offloat(target,VIEw.SCALE_Y,1f);    AnimatorSet enter = new AnimatorSet();    enter.setTarget(target);    enter.setInterpolator(new linearInterpolator());    enter.setDuration(500).playTogether(Alpha,scaleX,scaleY);    return enter;  }

贝塞尔曲线动画

  private ValueAnimator getBezIErValueAnimator(final VIEw target) {    // 初始化贝塞尔估值器    BezIErEvaluator evaluator = new BezIErEvaluator(getPointF(2),getPointF(1));    // 起点在底部中心位置,终点在底部随机一个位置    ValueAnimator animator = ValueAnimator.ofObject(evaluator,new PointF((mWIDth - DWIDth) /        2,mHeight - dHeight),new PointF(random.nextInt(getWIDth()),0));    animator.setTarget(target);    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @OverrIDe      public voID onAnimationUpdate(ValueAnimator valueAnimator) {        // 这里获取到贝塞尔曲线计算出来的的x y值 赋值给vIEw 这样就能让爱心随着曲线走啦        PointF pointF = (PointF) valueAnimator.getAnimatedValue();        target.setX(pointF.x);        target.setY(pointF.y);        // Alpha动画        target.setAlpha(1 - valueAnimator.getAnimatedFraction());      }    });    animator.setDuration(3000);    return animator;  }

结合动画添加爱心

public voID addHeart() {    final ImageVIEw imageVIEw = new ImageVIEw(getContext());    // 随机选一个爱心    imageVIEw.setimageDrawable(drawables[random.nextInt(3)]);    imageVIEw.setLayoutParams(lp);    addVIEw(imageVIEw);    AnimatorSet finalSet = new AnimatorSet();    AnimatorSet enteranimatorSet = getEnteranimator(imageVIEw);//入场动画    ValueAnimator bezIErValueAnimator = getBezIErValueAnimator(imageVIEw);//贝塞尔曲线路径动画    finalSet.playSequentially(enteranimatorSet,bezIErValueAnimator);    finalSet.setInterpolator(mInterpolators[random.nextInt(4)]);    finalSet.setTarget(imageVIEw);    finalSet.addListener(new AnimatorListenerAdapter() {      @OverrIDe      public voID onAnimationEnd(Animator animation) {        super.onAnimationEnd(animation);        removeVIEw((imageVIEw));//删除爱心      }    });    finalSet.start();  }

3.3 贝塞尔曲线侧边索引条

实例地址:FancyListIndexer_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

总结

以上是内存溢出为你收集整理的android中贝塞尔曲线的应用示例全部内容,希望文章能够帮你解决android中贝塞尔曲线的应用示例所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1146630.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存