贝塞尔曲线
贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。主要结构:起始点、终止点(也称锚点)、控制点。通过调整控制点,贝塞尔曲线的形状会发生变化。
了解一下贝塞尔曲线,根据影响变量的个数不同,我们可以看到不同类型的曲线
一阶贝塞尔曲线(线段):
公式:
意义:由 P0 至 P1 的连续点, 描述的一条线段
二阶贝塞尔曲线(抛物线):
公式:
原理: 由 P0 至 P1 的连续点 Q0,描述一条线段。
由 P1 至 P2 的连续点 Q1,描述一条线段。
由 Q0 至 Q1 的连续点 B(t),描述一条二次贝塞尔曲线。
三阶贝塞尔曲线:
当然还有四阶曲线、五阶曲线......只不过随着变量的增加,复杂维度会越来越高
虽然从公式上理解是非常难得,我们在开发中,也不是必须要完全理解这些公式,大概知道原理即可,通过这篇文章,我们可以大概理解它的图形上面的变化实现 http://www.html-js.com/article/1628
这个工具网站,可以帮助我们去绘制贝塞尔曲线 http://bezier.method.ac/#
用自定义view实现:
public class BezierClass extends View { private Paint mPaint; private Path mPath; private Paint paint; private int viewWidth, viewHeight; //控件的宽和高 private float commandX, commandY; //控制点的坐标 private float waterHeight; //水位高度 private boolean isInc; // 判断控制点是该右移还是左移 @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); viewWidth = w; viewHeight = h; commandY = 7 / 8f * viewHeight; //红色辅助线 waterHeight = 15 / 16f * viewHeight; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //起始点位置 mPath.moveTo(-1/4f*viewWidth,waterHeight); //水波浪 mPath.quadTo(commandX,commandY,viewWidth+1/4f*viewWidth,waterHeight); //水波浪下方闭合区域 mPath.lineTo(viewWidth+1/4f*viewWidth,viewHeight); mPath.lineTo(-1 / 4F * viewWidth, viewHeight); mPath.close(); //绘制路径 canvas.drawPath(mPath, mPaint); //绘制红色水位高度辅助线 canvas.drawLine(0,waterHeight,viewWidth,waterHeight,paint); //产生波浪左右涌动的感觉 if (commandX >= viewWidth + 1 / 4F * viewWidth) { //控制点坐标大于等于终点坐标改标识 isInc = false; } else if (commandX <= -1 / 4F * viewWidth) { //控制点坐标小于等于起点坐标改标识 isInc = true; } commandX = isInc ? commandX + 20 : commandX - 20; //水位不断加高 当距离控件顶端还有1/8的高度时,不再上升 if (commandY >= 1 / 8f * viewHeight) { commandY -= 2; waterHeight -= 2; } //路径重置 mPath.reset(); // 重绘 invalidate(); } /** * 测量 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // int wSpecMode = MeasureSpec.getMode(widthMeasureSpec); // int wSpecSize = MeasureSpec.getSize(widthMeasureSpec); // int hSpecMode = MeasureSpec.getMode(heightMeasureSpec); // int hSpecSize = MeasureSpec.getSize(heightMeasureSpec); // // if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) { // setMeasuredDimension(300, 300); // } else if (wSpecMode == MeasureSpec.AT_MOST) { // setMeasuredDimension(300, hSpecSize); // // } else if (hSpecMode == MeasureSpec.AT_MOST) { // setMeasuredDimension(wSpecSize, 300); // } } public BezierClass(Context context) { super(context); init(); } public BezierClass(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public BezierClass(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } /** * 初始化画笔 路径 */ private void init() { //画笔 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.parseColor("#AFDEE4")); //路径 mPath = new Path(); //辅助画笔 paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.RED); paint.setStrokeWidth(5f); } }在mainxml文件布局:
<test.bwei.com.beisaier.BezierClass android:id="@+id/bc" android:layout_width="match_parent" android:layout_height="match_parent" /> mainActivity主方法: 《主方法没有逻辑》
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }