贝塞尔曲线

2020-06-30  本文已影响0人  echoSuny

在Path的系列函数中,除了一些基本的设置和绘图方法外,还有一个比较强大的功能就是贝塞尔曲线。它能够将moveTo和lineTo连接的生硬路径变得平滑,能够实现比较多炫酷的效果。

贝塞尔曲线公式

一阶贝塞尔曲线

一阶贝塞尔曲线公式
ps: 属于符号∈是数学中用于表示某个数值在某个区间或某个集合当中
P0为起始点,P1为终点,t表示当前时间,B(t)表示公式的结果值,最后的t∈ [0, 1]表示t<=0<=1。
一阶贝塞尔曲线动画演示
黑色的点表示在不同的时间t下,B(t)的值,红线则表示随着时间的移动,B(t)所形成的轨迹。
据图可知一阶贝塞尔曲线就是一条在起点和终点形成的直线上匀速移动的点
二阶贝塞尔曲线
二阶贝塞尔曲线公式
二阶贝塞尔曲线动画演示
P0是起点,P2是终点,P1是控制点。首先P0和P1形成了一条一阶贝塞尔曲线,上面说了一阶贝塞尔曲线就是在直线上作匀速运动,所以称在P0-P1这条直线上匀速运动的点为O1。同理在P1-P2上也有一个匀速运动的点为O2。最后O1和O2又形成了一条一阶贝塞尔曲线,在这条贝塞尔曲线上移动的点就是上图中的黑点,称之为O3。O3最终的轨迹就是上图中的红线。
三阶贝塞尔曲线
三阶贝塞尔曲线公式
三阶贝塞尔曲线动画显示
P0是起点,P3是终点,P1是第一个控制点,P2是第二个控制点。首先有三条一阶贝塞尔曲线P0-P1,P1-P2,P2-P3,那么就会分别对应有三个在这三条直线上迅速运动的点O1,O2,O3。这三个点会再次形成两个一阶贝塞尔曲线O1-O2和O2-O3。同理在这两条一阶贝塞尔曲线上又会有两个匀速的点M1和M2,则这两个点又会形成一条一阶贝塞尔曲线,这条线上的点就是上图中的黑点,最终随着时间的推移,就会形成红色的轨迹。
另外还有四阶贝塞尔曲线和五阶贝塞尔曲线,Android只支持到三阶贝塞尔曲线,但原理都是一样的。
二阶贝塞尔曲线
        val path = Path()
        path.moveTo(100f,400f)
        path.quadTo(150f,150f,300f,400f)
        path.quadTo(450f,550f,600f,400f)
        canvas.drawPath(path,paint)
效果图

下面实现一个根据手势绘制曲线的简单例子:

override fun onTouchEvent(event: MotionEvent): Boolean {

        if (event.action == MotionEvent.ACTION_MOVE) {
        }
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                downX = event.x
                downY = event.y
                path.moveTo(downX, downY)
                return true
            }
            MotionEvent.ACTION_MOVE -> {
                val endX = (downX + event.x) / 2
                val endY = (downY + event.y) / 2
                path.quadTo(downX, downY, endX, endY)
                downX = event.x
                downY = event.y
                invalidate()
            }
            else -> {
                // do noting
            }
        }
        return super.onTouchEvent(event)
    }
        path.moveTo(100f,400f)
        path.rQuadTo(50f,-250f,200f,0f)
        path.rQuadTo(150f,150f,300f,0f)
        canvas.drawPath(path, paint)

可以看到和上面是用quadTo()函数实现的效果是一样的。前面说了rQuatTo()是相对于上一个终点计算的。显然在这段代码中上一个点是(100,400)。那控制点的绝对坐标就应该是(100+50,400-250)也就是(150,150),终点的坐标就应该是(100+200,400+0),即(300,400)。这与上面使用quatTo()时传入的坐标path.quadTo(150f,150f,300f,400f)是对应的。此时终点就成了(300,400),下面就可以计算path.rQuadTo(150f,150f,300f,0f)的绝对坐标了。那么控制点就为(300+150,400+150) = (450,550),终点则为(300+300,400+0) = (600,400),与quatTo()中 path.quadTo(450f,550f,600f,400f)一致,故最后画出的曲线是一致的。

    fun startWave() {
        val animator = ValueAnimator.ofFloat(0f, mWaveLength)
        animator.duration = 3000
        animator.repeatCount = ValueAnimator.INFINITE
        animator.interpolator = LinearInterpolator()
        animator.addUpdateListener {
            dx = it.animatedValue as Float
            postInvalidate()
        }
        animator.start()
    }

    override fun onDraw(canvas: Canvas) {
            super.onDraw(canvas)
        path.reset() 
        val originY = 300f 
        val halfWaveLength = mWaveLength / 2 
        path.moveTo(-mWaveLength + dx, originY) 
        val total = width + mWaveLength
        for (i in (-mWaveLength).toInt()..total.toInt() step mWaveLength.toInt()) {
            path.rQuadTo(halfWaveLength / 2, -100f, halfWaveLength, 0f)
            path.rQuadTo(halfWaveLength / 2, 100f, halfWaveLength, 0f)
        }
        path.lineTo(width.toFloat(), height.toFloat())
        path.lineTo(0f, height.toFloat())
        path.close()
        canvas.drawPath(path, paint)
    }

根据上面这幅简图来解释一下上面的代码:
path.moveTo(-mWaveLength + dx, originY) 就是把起点设在最左边红色箭头指的坐标
val total = width + mWaveLength 相当于在屏幕外的右侧画来一个和左边一样的波行
for循环就是把所有的路径都连起来,重点是for 循环里的内容
path.rQuadTo(halfWaveLength / 2, -100f, halfWaveLength, 0f)这句代码的前两个参数就是上图中的第一个黑点的坐标,是贝塞尔曲线的控制点。由于起始点的Y坐标为300,而rQuadTo()是相对于上一个点的,则求出来的Y坐标值则是300+(-100) = 200。后两个参数则是下面右边蓝色箭头所指的位置,是贝塞尔曲线的终点。
path.rQuadTo(halfWaveLength / 2, 100f, halfWaveLength, 0f) 这句代码和上一句的逻辑是一样的,只不过是求的后一半的波长中的控制点和终点。
最后的调用Path函数的三行代码只是把整个Path闭合了,如下图,所以在屏幕上是看不见的:



而startWave()就只是启动了一个无限循环的属性动画,并监听动画的值,然后不断的改变moveTo()的坐标来达到让整个曲线动起来的效果

三阶贝塞尔曲线
        path.moveTo(100f,400f)
        path.cubicTo(150f,150f,350f,550f,600f,400f)
        canvas.drawPath(path, paint)
上一篇 下一篇

猜你喜欢

热点阅读