文章目录

  • 1. 前言
  • 2. 介绍
    • 2.1 一阶贝济埃曲线
    • 2.2 二阶贝塞尔曲线
    • 2.3 三阶贝塞尔曲线
  • 3. 一、二、三阶贝塞尔曲线实现
  • 4. 案例
  • 5. 后记

1. 前言

贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。

2. 介绍

2.1 一阶贝济埃曲线

一阶贝济埃曲线的公式如下:

 B(t)=(1-t)P_0+tP_1,t属于0-1

P0为起始点,P1为终点,t 表示当前时间,B(t)表示公式的结果值。其实也就是一条从P0到P1的直线上,匀速运动的点值。

2.2 二阶贝塞尔曲线

这条曲线的构成也就是每个t时刻,Q0和Q1的所属的直线的的t时刻的距离的点,这里也就是B。不妨将上面这个图简单标注下:

也就是在从P0到P1,进行匀速运动,在t=0.25的时刻走到Q0,类似的,从P1到P2经过匀速运动,在t=0.25的时刻走到Q1,对于Q0到Q1,经过匀速运动,在t=0.25的时刻走到B。而B也就是二阶贝塞尔曲线上的点。

2.3 三阶贝塞尔曲线

也就是说此时有两个控制点,对应着也就是三根连着的线段,类似的我们可以得到最终的t点:

那么,根据上面的规则,我们可以自己来实现一下贝赛尔曲线的计算方式,并将曲线绘制出来。

3. 一、二、三阶贝塞尔曲线实现

定义为:

class Point(var x: Float, var y: Float){}/*** 得到贝赛尔曲线上的点集* @param points 起始、控制和终止点坐标* @param number 需要计算的贝赛尔曲线上的点的个数* @return 返回路径*/
private fun getBezierPointsPath(points: Array<Point>, number: Int): Path{val path = Path()for (time in 0 until number){val t = time * 1f / numberval point = calcPoint(points, t)if(time == 0){path.moveTo(point.x, point.y)} else {path.lineTo(point.x, point.y)}Log.e("TAG", "getBezierPointsPath: ${point.x} , ${point.y}", )}return path
}/*** 计算在t时刻上,位于贝赛尔曲线上的点的坐标* @param points 点的集合* @param t 时刻,属于0-1* @return 点坐标 Point*/
private fun calcPoint(points: Array<Point>, t: Float): Point{// 分别求任意两个点之间的在t时刻运动的距离// 任意两点,按照顺序分别为始和终var index = 0var len = points.size - 1while (index < len){points[index].x = getValueByTime(points[index].x, points[index + 1].x, t)points[index].y = getValueByTime(points[index].y, points[index + 1].y, t)index++if(index == len){index = 0len--}}return points[0]
}/*** 定义匀速运动的计算坐标* @param start 开始的位置* @param end 结束的位置* @param time 运动的时间,范围0-1* @return time时刻的运动位置*/
private fun getValueByTime(start: Float, end: Float, time: Float): Float{return start + (end - start) * time
}

然后使用:

// 绘图方法
override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)canvas?.apply {val points = arrayOf(Point(200f, 400f), Point(100f, 20f), Point(500f, 20f), Point(800f, 400f))val numberOfPoint = 100mPath = getBezierPointsPath(points, numberOfPoint)drawPath(mPath, mPaint)}
}

很明显,这里细粒度不够。可以把numberOfPoint 设置的更大些。当设置为1000的时候:

当然这里可以使用arrayOf的时候添加更多的点,以做到更加高阶的贝塞尔曲线,比如简单修改一下:

val points = arrayOf(Point(200f, 400f),Point(100f, 20f),Point(500f, 20f),Point(800f, 400f),Point(1000f, 20f)
)

也就是对应三个控制点,对应四阶本塞尔曲线,对应效果:

当然,在系统中其实也提供了一、二、三阶的贝赛尔曲线的API,所以通常直接调用即可。对应的如下:

  • mPath.lineTo:进行直线绘制 ;
  • mPath.quadTo(x1, y1, x2, y2) :生成二次贝塞尔曲线,(x1,y1) 为控制点,(x2,y2)为结束点 ;
  • mPath.cubicTo(x1, y1, x2, y2, x3, y3):生成三次贝塞尔曲线, (x1,y1) 为控制点,(x2,y2)为控制点,(x3,y3) 为结束点;

4. 案例

/*** 学习波浪效果,其实也就是移动类似于正弦的连续图像,带来的视觉效果* @author 梦否* 2022年3月15日*/
class WaterRippleView : View {constructor(context: Context?) : super(context) {init()}constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {init()}constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr) {init()}private lateinit var mPath: Pathprivate lateinit var mPaint: Paintprivate lateinit var points1: Array<MyPoint>private lateinit var points2: Array<MyPoint>class MyPoint(var x: Float, var y: Float)/*** 初始化方法*/private fun init() {mPath = Path()mPaint = Paint()mPaint.isDither = truemPaint.isAntiAlias = truemPaint.strokeWidth = 5fmPaint.color = Color.GRAYmPaint.style = Paint.Style.FILLval viewWidth = resources.displayMetrics.widthPixelspoints1 = arrayOf(MyPoint(0f * viewWidth, 200f),MyPoint(.33f * viewWidth, 20f),MyPoint(.66f * viewWidth, 360f),MyPoint(1f * viewWidth, 200f))points2 = arrayOf(MyPoint(-1f * viewWidth, 200f),MyPoint(-.66f * viewWidth, 20f),MyPoint(-.33f * viewWidth, 360f),MyPoint(0f * viewWidth, 200f),)// 三阶贝塞尔曲线,传入0,也就是初始时刻updatePathByDistance(0f)}override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)canvas?.apply {drawPath(mPath, mPaint)}}/*** 根据距离来进行更新在贝赛尔曲线中的点的坐标值* @param distance 传入的距离*/private fun updatePathByDistance(distance: Float) {// 重置mPath.reset()// 设置mPath.moveTo(points2[0].x, points2[0].y)mPath.cubicTo(points2[1].x + distance,points2[1].y,points2[2].x + distance,points2[2].y,points2[3].x + distance,points2[3].y)mPath.cubicTo(points1[1].x + distance,points1[1].y,points1[2].x + distance,points1[2].y,points1[3].x + distance,points1[3].y)val y = resources.displayMetrics.heightPixelsmPath.lineTo(points1[3].x, y.toFloat())mPath.lineTo(points2[0].x + distance,  y.toFloat())mPath.lineTo(points2[0].x + distance, points2[0].y)}/*** 一直移动绘制的两个类似于正弦函数的路径*/var startedMove = falseprivate fun startMove() {startedMove = trueval animator = ValueAnimator.ofFloat(0f, resources.displayMetrics.widthPixels.toFloat())animator.duration = 800// 线性插值器,使之匀速运动animator.interpolator = LinearInterpolator()// 循环animator.repeatCount = ValueAnimator.INFINITEanimator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {override fun onAnimationUpdate(animation: ValueAnimator?) {val value = animator.getAnimatedValue()updatePathByDistance(value as Float)// 重绘invalidate()}})animator.start()}override fun onTouchEvent(event: MotionEvent?): Boolean {super.onTouchEvent(event)var flag = falsewhen (event?.action) {MotionEvent.ACTION_DOWN -> {flag = trueif(!startedMove) startMove()}MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP -> {flag = false}}return flag}override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)val minHeight = dp2px(300)val minWidth = dp2px(500)val widthSize = getMeasureSize(widthMeasureSpec, minWidth.toInt())val heightSize = getMeasureSize(heightMeasureSpec, minHeight.toInt())setMeasuredDimension(widthSize, heightSize)}/*** 计算高度和宽度*/private fun getMeasureSize(Spec: Int, minValue: Int): Int {var result = 0// 获取模式val mode = MeasureSpec.getMode(Spec)val size = MeasureSpec.getSize(Spec)// 判断一下when (mode) {MeasureSpec.AT_MOST -> {result = Math.min(size, minValue)}MeasureSpec.UNSPECIFIED -> {result = minValue}MeasureSpec.EXACTLY -> {result = size}}return result}/*** dp转换为px*/private fun dp2px(size: Int): Float {return resources.displayMetrics.density * size}
}

5. 后记

当然关于贝赛尔曲线的应用远不止如此。比如:Android开发之贝塞尔曲线进阶篇(仿直播送礼物,饿了么购物车动画),感兴趣的可以查阅原文。

贝塞尔曲线及实践案例相关推荐

  1. android 贝塞尔曲线_OpenGL 实践之贝塞尔曲线绘制

    说到贝塞尔曲线,大家肯定都不陌生,网上有很多关于介绍和理解贝塞尔曲线的优秀文章和动态图. 以下两个是比较经典的动图了. 二阶贝塞尔曲线: 三阶贝塞尔曲线: 由于在工作中经常要和贝塞尔曲线打交道,所以简 ...

  2. 贝塞尔曲线动画C++简单实践

    目录 贝塞尔曲线简介 一阶贝塞尔 二阶贝塞尔 三阶贝塞尔 N阶贝塞尔曲线 贝塞尔曲线在动画中的应用 实践 求曲线散点坐标 将曲线应用到动画 动画框架 cmd动画 窗口动画 完整代码 示例代码 核心类代 ...

  3. android sin 曲线,Android 贝塞尔曲线实践——旋转的七色花朵

    一.关于贝塞尔曲线 在工业设计方面贝塞尔曲线有很多用途,同样,在Android中,贝塞尔曲线结合Path类可以实现更复杂的图形,这里我们给一个案例,来实现一种旋转的花朵.对于贝赛尔曲线的理解,建议参考 ...

  4. Flutter 贝塞尔曲线实现案例

    前言: 各位同学大家好,有段时间没有给大家更新文章了,趁着今天有时间我们就给大家分享一个flutter的贝塞尔曲线的绘制的案例,希望能帮助到各位同学的学习和工作,那么废话不多说我们正式开始 . 准备工 ...

  5. css贝塞尔曲线 多个点_贝塞尔曲线实践

    贝塞尔曲线: 贝塞尔曲线本质上是由线段和节点组成的,形象的说节点是可拖动的支点,线段像可伸缩的皮筋.一个常规的曲线往往由4个控制点构成(p0,p1,p2,p3),曲线经过起点(p0)和终点(p1). ...

  6. 技术分享 | 一条神奇的曲线——贝塞尔曲线在前端的应用

    源宝导读:在前端的开发中我们经常会遇到利用贝塞尔曲线帮助我们完成前端的动画和图形绘制,但是对其中的一些参数配置是一头雾水.本文将从贝塞尔曲线的原理讲起,由浅入深剖析一阶到多阶贝塞尔的实现原理,最后从三 ...

  7. 贝塞尔曲线轨迹运动原理与实战

    大厂技术  坚持周更  精选好文 本次分享大概分为下面几个方面 背景 贝塞尔曲线讲解 实现和探索过程 背景 近期在 X 业务测评报告页有一个需求,用户可以左右拖动滑块来查看各个等级的信息. 在之前的野 ...

  8. html贝塞尔曲线在线,贝塞尔曲线的一些事情_html/css_WEB-ITnose

    贝塞尔曲线(Bezier curves)是曲率的一种典型代表,而且在很多应用中都会运用到,比如计算机的图形学中.字体和动画.如果你以前玩过CSS,那么你可能就运到过贝塞尔曲线.例如,在CSS的时间函数 ...

  9. html5贝塞尔曲线,Canvas学习:贝塞尔曲线

    在绘制圆和圆弧一节中,了解到在Canvas中可以使用arc()和arcTo()绘制制圆或弧线,但很多时候,仅这两个方法还不能满足我们实际的需求,特别是绘制复杂的曲线.不过值得庆幸的是,在Canvas中 ...

  10. android 贝塞尔曲线点击区域,白话经典贝塞尔曲线及其在 Android 中的应用

    一.前言 谈到贝塞尔曲线可能不少人会浮现它高大上的数学公式.然而,在实际应用中,并不需要我们去完全理解或者推导出公式才能应用得上.实际情况是,即使真的只是一个学渣,我们应该也能很轻松的掌握贝塞尔曲线的 ...

最新文章

  1. [hive] hive 内部表和外部表
  2. passwd 修改用户密码
  3. ubuntu9.10硬盘安装记录二
  4. MYSQL数据库注释
  5. 【C++进阶】 遵循TDD原则,实现平面向量类(Vec2D)
  6. java空值转datetime,解决Java (Spring boot) 读取数据库字段,datetime 格式为null,抛出异常 Zero date value prohibited...
  7. X264学习笔记(1)
  8. git在eclipse中的配置 转载
  9. 百度总裁张亚勤十月退休:感谢李彦宏和百度 很高兴能安心退休
  10. LeNet-5实战minist——搭建卷积网络模型
  11. php伪静态限制网页播放视频,php伪静态后html不能访问怎么办
  12. php蓝奏云网盘源码,蓝奏云网盘pc版易语言开源软件源码
  13. 关于编程, 游戏, 学习, 人生和奥特曼的感想(持续更新)
  14. python itchat_Python使用itchat获取微信好友
  15. 钓鱼网站检测技术的演进
  16. 经典回忆Adobe Photoshop CS 2安装教程永久注册使用
  17. 浅谈Linux PMIC驱动(一)
  18. JavaScript 学习笔记 - 12 JavaScript 应用示例
  19. linux添加定时任务报错 error renaming /var/spool/cron/#tmp.xxxxxxxx to /var/spool/
  20. 求一个数组的非空子集

热门文章

  1. 八大流行的微服务架构设计模式探究
  2. matlab 读取mdf文件路径,通过 MDF 数据存储使用 MDF 文件
  3. 关于开会了一点点想法
  4. [视频教程][斯坦福大学公开课:ios7应用开发 18集]
  5. 自然辩证法(研究生)期末考试题库
  6. QT24A01 TNK以太网变压器
  7. 使用ps工具进行图片分析
  8. 这些配色方案让数据可视化图表更加高大上
  9. tushare 获取复权数据
  10. AVFrame的数据填充方式