# 一张图带你彻底了解二阶贝塞尔曲线

*本篇文章已授权微信公众号 guolin_blog （郭霖）独家发布

moveTo(float x,float y)

quadTo(float x1, float y1, float x2, float y2 )

x2,y2的坐标就是图中曲线右边终点的位置坐标


@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
path.reset();
//绘制二阶贝塞尔曲线
path.moveTo(mWidth * 1 / 8, mHeight * 1 / 5);
path.quadTo(xWidth, yHeight, mWidth * 7 / 8, mHeight * 1 / 5);
canvas.drawPath(path, paint);
paint.setStyle(Paint.Style.FILL);
//绘制控制点
canvas.drawCircle(xWidth, yHeight, 10, paint);
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
xWidth = x;
yHeight = y;
postInvalidate();
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}

1.重写onDraw()方法

        //确定小火箭控制点的范围
if (xWidth < xSize) {
xWidth = xSize;
}
if (xWidth > mWidth * 9 / 10) {
xWidth = mWidth * 9 / 10;
}
if (yHeight > mHeight * 8 / 10) {
yHeight = mHeight * 8 / 10;
}
if (yHeight > mHeight * 7 / 10 && xWidth < mWidth * 4 / 10) {
yHeight = mHeight * 7 / 10;
}
if (yHeight > mHeight * 7 / 10 && xWidth > mWidth * 6 / 10) {
yHeight = mHeight * 7 / 10;
}


xSize与ySize分别代表整体View宽度与高度的1/10，xWidth与yHeight是在action为MotionEvent.ACTION_MOVE时拿到的x与y坐标。后面绘制小火箭以及发射台的坐标都是基于这两个点进行改变的。仔细观察示例图效果，你会发现，小火箭是无法超出这个设置的区域。

        paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
path.reset();
//绘制小火箭
path.moveTo(xWidth - xSize * 1 / 2, yHeight - ySize * 3 / 5);
path.lineTo(xWidth, yHeight - ySize);
path.lineTo(xWidth + xSize * 1 / 2, yHeight - ySize * 3 / 5);
path.moveTo(xWidth - xSize * 1 / 4, yHeight - ySize * 4 / 5);
path.lineTo(xWidth - xSize * 1 / 4, yHeight);
path.lineTo(xWidth + xSize * 1 / 4, yHeight);
path.lineTo(xWidth + xSize * 1 / 4, yHeight - ySize * 4 / 5);
canvas.drawPath(path, paint);

        //绘制发射台
paint.setStrokeWidth(10);
arcPath.reset();
arcPath.moveTo(mWidth * 1 / 10, mHeight * 7 / 10);
if (yHeight > mHeight * 7 / 10 && xWidth > mWidth * 4 / 10 && xWidth < mWidth * 6 / 10) {
arcHeight = yHeight + yHeight - mHeight * 7 / 10;
} else {
arcHeight = mHeight * 7 / 10;
}
arcPath.quadTo(mWidth * 5 / 10, arcHeight, mWidth * 9 / 10, mHeight * 7 / 10);
canvas.drawPath(arcPath, paint);

        //绘制成功后的文字
if (isSuccess && yHeight < 0) {
txtPaint.setTextSize(80);
txtPaint.setColor(color);
txtPaint.getTextBounds(text, 0, text.length(), mRect);
canvas.drawText(text, mWidth * 1 / 2 - mRect.width() / 2, mHeight * 1 / 2 + mRect.height() * 1 / 2, txtPaint);
}

2.重写onTouchEvent(MotionEvent ev)方法

   @Override
public boolean onTouchEvent(MotionEvent ev) {

int action = ev.getAction();
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
isSuccess = false;
break;
case MotionEvent.ACTION_MOVE:
xWidth = x;
yHeight = y;
postInvalidate();
break;
case MotionEvent.ACTION_UP:
if (yHeight > mHeight * 7 / 10 && xWidth > mWidth * 4 / 10 && xWidth < mWidth * 6 / 10) {
startAnim();
}
break;
}
return true;
}

MotionEvent.ACTION_DOWN的时候，发射成功设置为false
MotionEvent.ACTION_MOVE的时候，获取触摸点的坐标，并赋值给控制点
MotionEvent.ACTION_UP的时候，进行判断，符合发射条件就开启动画

3.动画实现

    private void startAnim() {
//动画实现
ValueAnimator animator = ValueAnimator.ofInt(yHeight, -ySize);
@Override
public void onAnimationUpdate(ValueAnimator animation) {
yHeight = (int) animation.getAnimatedValue();
postInvalidate();
}
});
animSet.setDuration(1200);
animSet.play(animator);
animSet.start();
isSuccess = true;
}

OK，相信看到这里，你对二阶贝塞尔曲线有了更加全面的认识。下一篇自定义View再见~~

• 广告
• 抄袭
• 版权
• 政治
• 色情
• 无意义
• 其他

120