贝塞尔曲线学习:正弦曲线

前言内容

以前通过自定义view可以绘制出各种效果,但这些效果多数还是存在于规则的图像,今天学习贝塞尔曲线,来绘制一些更特别的线条。

简单来说贝塞尔曲线通过控制点,可以绘制出各种路径。一般我们常用的二阶贝塞尔和三阶贝塞尔(对应的控制点数量不同)。这也是Android提供给我们的方法。

网上介绍的资料很多,可以全面了解下。下面我用二阶贝塞尔曲线绘制一个正弦曲线,然后在让曲线动起来,来模仿波浪吧。
贝塞尔曲线学习:正弦曲线_第1张图片

内容部分

代码超级少,先从原理来简单分析下我们要做什么。

  1. 最重要的是计算绘制路线的点的集合,这里我们需要确定波开始的位置,和波峰波谷高度,还有就是波长

    下面隐藏波长只是为了,移动的时候看起来是连续的。

    注意:

    一个连续的波需要四个点来完成。最后一个波说五个点,然后因为需要移动所以我们多加了一个波。所以公式是 4*n+5,这里的n是波峰,一个完整的波由一个波峰和一个波谷组成。

     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec)
            if (!isMeasured) {
                isMeasured = true
                viewWidth = measuredWidth
                viewHeight = measuredHeight
                //底部开始
                mStartPoint = measuredHeight.toFloat() / 2
                //波峰高度
                mCrestHeight = viewHeight / 15f
                //波峰长度
                mCrestWidth = measuredWidth / 2
                //隐藏一个波长
                mLeftHide = -mCrestWidth.toFloat()
                //几个波峰
                val n = (viewWidth / mCrestWidth + 0.5).roundToInt()
                for (i in 0..n * 4 + 4) {
                  //x坐标开始的位置为负的一个波长开始
                    var x = (i * mCrestWidth / 4).toFloat() - mCrestWidth
                    var y = 0f
                    when (i % 4) {
                        0, 2 -> y = mStartPoint
                        1 -> y = mStartPoint + mCrestHeight
                        3 -> y = mStartPoint - mCrestHeight
                    }
                    pointList.add(Point(x, y))
                }
            }
        }
    
  2. 拿到运动的点的集合后就是绘制,这里需要调用quadTo方法传入两个点。然后是让绘制好曲线平移就可以看到波浪的效果了(很多动画效果都是通过改变view的坐标完成,特别是页面动画效果很多的时候)。

    这里是绘制一个path,主要是顶部是一个波浪的形状。

    注意:

    • 每两个点就绘制出一个半个正弦图了。所以循环是两个点为一组开始的
    • 然后绘制完成后要闭合找个path
    • 发送消息不断移动x的数值,就可以动起来了
    
        override fun onDraw(canvas: Canvas?) {
            mRipplePath.reset()
            mRipplePath.moveTo(pointList[0].x, pointList[0].y)
            Log.d("RippleView", pointList.size.toString())
    
            for (i in 0..(pointList.size - 3) step 2) {
                Log.d("RippleView", i.toString())
                mRipplePath.quadTo(pointList[i + 1].x, pointList[i + 1].y, pointList[i + 2].x, pointList[i + 2].y)
            }
            mRipplePath.lineTo(pointList.get(pointList.size - 1).getX(), viewHeight.toFloat())
            mRipplePath.lineTo(mLeftHide, viewHeight.toFloat())
            mRipplePath.close()
            canvas!!.drawPath(mRipplePath, mPaint)
            mHandler.sendEmptyMessageDelayed(1, 10)
        }
    

总的来说就是上面两个步骤。

其实没什么内容,因为很简单。

GitHub传送门

结束部分

Android内容有点多,总之还是学点是点吧。各位加油

你可能感兴趣的:(自定义的view)