前言:

贝塞尔曲线又称贝兹曲线,它的主要意义在于无论是直线或曲线都能在数学上予以描述。最初由保罗·德卡斯特里奥(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 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, 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);

}

private ArrayList buildBezierPoints() {

ArrayList 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, 0, t), getBezierPointY(order, 0, 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, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y);

canvas.drawPath(mPath, 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 {

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, 0.2f, 1f);

ObjectAnimator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y, 0.2f, 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 贝塞尔曲线侧边索引条

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

android贝塞尔曲线实例,android中贝塞尔曲线的应用示例相关推荐

  1. android 图库开发实例,Android中从图库中选取图片实例详解

    android 从图库中选取图片 在android中,如何从图库gallary中挑选图片呢,其实很简单,步骤如下 1) 设计一个imageview,用来显示图库选出来的图片 android:orien ...

  2. android 拨打电话 实例,Android实战教程第三篇之简单实现拨打电话功能

    本文实例为大家分享了Android打电话功能的实现代码,需要一个文本输入框输入号码,需要一个按钮打电话. 本质:点击按钮,调用系统打电话功能. xml布局文件代码:: xmlns:tools=&quo ...

  3. android 属性动画实例,Android 属性动画Animator工具类代码案例

    代码分享-> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ packa ...

  4. android播放器实例,android音乐播放器实例

    郑州app开发android音乐播放器实例.布局代码是一个imagebutton和seekbar. 下面是java代码 MainActivity.java package cn.xhhkj.music ...

  5. android 属性动画实例,Android属性动画完全解析 中 ,ValueAnimator和ObjectAnimator的高级用法...

    大家好,在上一篇文章当中,我们学习了Android属性动画的基本用法,当然也是最常用的一些用法,这些用法足以覆盖我们平时大多情况下的动画需求了.但是,正如上篇文章当中所说到的,属性动画对补间动画进行了 ...

  6. coreldraw x4曲线成长度_CorelDraw中获取曲线的长度和面积

    在CorelDraw中绘图有时需要获取所绘曲线的长度和面积,但是CorelDraw中并没有CAD中那样方便的查看属性的工具,这里将介绍一种查看长度和面积的方法. 打开CorelDraw,新建文档绘制一 ...

  7. android sp wp实例,android sp wp详解

    研究的时候,经常会遇到sp.wp的东西,网上一搜,原来是android封装了c++中对象回收机制. 说明: 1. 如果一个类想使用智能指针,那么必须满足下面两个条件: a. 该类是虚基类RefBase ...

  8. android rfid开发实例,Android NFC读卡 高频卡 RFID

    [实例简介] Android NFC读卡 高频卡 RFID 仅供参考,有需要这方面资料的可以联系我 961500278@qq.com [实例截图] [核心代码] d303bda7-45a0-4b95- ...

  9. android studio编程实例,Android Studio JNI 开发简单案例

    进程保活,热修复,硬件接入等等都需要底层的支持,而底层代码是 C .C++ 写的,那么在 Android 中怎么调用底层的库呢?这里就需要了解 JNI 技术. Android Studio 出来两年多 ...

最新文章

  1. 在线作图|如何绘制一张坡度图
  2. cursor_sharing用法
  3. babel import语法 js_Babel 的理解
  4. 整型数组 判断 java,给定一个整数数组,判断其中是否有3个数和为N
  5. cookies与session
  6. android handler的机制和原理_第一百八十回:Android中的Handler机制九
  7. windows照片查看器没了_装机必备|Windows 上用得最爽的18款高效软件
  8. JS简单的时间控件分享
  9. mac paralles内 windows虚机 连接 linux虚机
  10. Windows bat脚本获取administrator权限
  11. 华为服务器系统日志,系统日志服务器
  12. requests 已过时,初探协程与异步 http 框架 httpx
  13. 计算机逻辑运算实验总结,算术逻辑运算实验报告(共10篇).doc
  14. php工程师是干什么的
  15. 安装MySQL报错:[Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defau
  16. 【Rust日报】2020-11-09 构建可测试性的 Rust 工程
  17. 谷歌支付获取refresh token
  18. 递归神经网络 训练、推到、实现(网络资料整理)_codestorm_新浪博客
  19. 实操 | 合并VCF文件的几种方法及注意事项
  20. 数值分析-秦九韶算法

热门文章

  1. 软阈值(soft-thresholding)函数
  2. 开源大数据周刊-第52期
  3. Mc1.16forge官混教程/教补-#4 为物品赋予常用属性
  4. 【UE4 C++】获取运行时间、设置时间流速、暂停游戏
  5. 28377D Bootloader
  6. form表单数字校验(二)——邮箱校验-当前页面
  7. 龙芯3B处理器—地址映射以及路由地址分布与配置
  8. 浅谈代理服务器工作的原理
  9. 通过港交所聆讯,背靠微创医疗的机器人能否成为国版“达芬奇”?
  10. 雅虎欧洲推Yahoo! Go 3.0程序和移动器件