贝塞尔曲线简介

参考:

Android-贝塞尔曲线
贝塞尔曲线扫盲

动态绘制贝塞尔曲线的在线演示

一阶贝塞尔曲线(线段):

包含两个点:起点、终点,
其实就是lineTo的效果。

在这里插入图片描述
在这里插入图片描述

二阶贝塞尔曲线(抛物线):

包含三个点:起点A、一个控制点B、终点C

在这里插入图片描述
在这里插入图片描述

长度关系为:

AD / DB = BE / EC = DF / FE

二阶曲线对应的方法是quadTo

Path mPath = new Path();
mPath.moveTo(x0,y0);//起点(x0,y0)
mPath.quadTo(x1,y1,x2,y2);
//控制点B坐标(x1,y1),决定了弧度方向与大小,
//终点C坐标(x2,y2)
canvas.drawPath(mPath, mPaint); 

示例:

public class MyView extends View {

    private float mSupX;
    private float mSupY;

    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(10);

        Path path = new Path();
        path.moveTo(200, 200);//起点
        path.quadTo(mSupX, mSupY, 800, 1300);//控制点,终点
        canvas.drawPath(path, paint);//画出贝塞尔曲线

        paint.setColor(Color.parseColor("#ff0000"));
        paint.setStrokeWidth(20);
        canvas.drawPoint(mSupX, mSupY, paint);//画出控制点
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                mSupX = event.getX();
                mSupY = event.getY();
                invalidate();
        }
        return true;
    }
}

效果图:

这里写图片描述

参考:Android自定义view进阶-- 神奇的贝塞尔曲线

三阶贝塞尔曲线:

包含四个点:起点A、2个控制点BC、终点D

在这里插入图片描述
在这里插入图片描述

长度关系为:

AE:AB = BF:BC = CG:CD = 
EH:EF = FI:FG = 
HJ:HI

三阶曲线对应的方法是cubicTo

path.cubicTo(0, 150, 300, 450, 0, 600);      
//(0, 150):控制点B,
//(300, 450):控制点C,
//(0, 600):终点D

三阶曲线计算过程与二阶类似,具体可以见下图动态效果:

示例:

public class MyView extends View {

    private Point controlPointOne = new Point(200, 200);
    private Point controlPointTwo = new Point(500, 200);

    private boolean isControlPointTwo;

    private Paint paintBezier;
    private Paint paintLine;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);


        paintBezier = new Paint();
        paintBezier.setStyle(Paint.Style.STROKE);
        paintBezier.setColor(Color.BLACK);
        paintBezier.setStrokeWidth(10);


        paintLine = new Paint();
        paintLine.setStyle(Paint.Style.STROKE);
        paintLine.setColor(Color.RED);
        paintLine.setStrokeWidth(3);


    }

    @Override
    protected void onDraw(Canvas canvas) {
        Path path = new Path();//注意path不可在构造方法中new

        path.moveTo(100, 500);
        if (isControlPointTwo) {
            paintBezier.setColor(Color.RED);
        } else {
            paintBezier.setColor(Color.BLUE);
        }
        path.cubicTo(controlPointOne.x, controlPointOne.y, controlPointTwo.x, controlPointTwo.y, 900, 500);
        //绘制路径
        canvas.drawPath(path, paintBezier);

        //绘制辅助点
        paintBezier.setColor(Color.RED);
        canvas.drawPoint(controlPointOne.x, controlPointOne.y, paintBezier);
        paintBezier.setColor(Color.BLUE);
        canvas.drawPoint(controlPointTwo.x, controlPointTwo.y, paintBezier);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                if (isControlPointTwo) {
                    controlPointOne.x = (int) event.getX();
                    controlPointOne.y = (int) event.getY();
                } else {
                    controlPointTwo.x = (int) event.getX();
                    controlPointTwo.y = (int) event.getY();
                }
                invalidate();
                break;
        }
        return true;
    }


    public boolean isControlPointTwo() {
        return isControlPointTwo;
    }

    public void setControlPointTwo(boolean controlPointTwo) {
        isControlPointTwo = controlPointTwo;
    }
}

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="切换控制点" />

    <com.android.imooc.MyView
        android:id="@+id/my_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>

MyActivity

public class MyActivity extends FragmentActivity {

    private Button btn;
    private MyView myView;
    private boolean isControlPointTwo = false;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test3);

        btn = ((Button) this.findViewById(R.id.btn));
        myView = ((MyView) this.findViewById(R.id.my_view));
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isControlPointTwo) {
                    myView.setControlPointTwo(false);
                } else {
                    myView.setControlPointTwo(true);
                }
                isControlPointTwo = !isControlPointTwo;
            }
        });
    }

}

效果图:

这里写图片描述

参考:Android – 贝塞尔曲线公式的推导和简单使用

demo:

public class MyView extends View {

    private final Path mPath;
    private final Paint mPaint2;
    private final Paint mPaint1;

    private final PointF mStartPoint = new PointF();
    private final PointF mControlPoint1 = new PointF();
    private final PointF mControlPoint2 = new PointF();
    private final PointF mEndPoint = new PointF();

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPath = new Path();

        mPaint2 = new Paint();
        mPaint2.setColor(Color.BLUE);
        mPaint2.setStyle(Paint.Style.FILL);
        mPaint2.setStrokeWidth(10);

        mPaint1 = new Paint();
        mPaint1.setColor(Color.RED);
        mPaint1.setStyle(Paint.Style.STROKE);
        mPaint1.setStrokeWidth(2);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        int rate = Math.min(w, h);
        mStartPoint.set(0, rate);
        mControlPoint1.set(0.2f * rate, 0.8f * rate);
        mControlPoint2.set(0.1f * rate, 0);
        mEndPoint.set(rate, 0);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //绘制背景
        canvas.drawColor(Color.GREEN);

        //绘制三阶贝塞尔曲线
        mPath.reset();
        mPath.moveTo(mStartPoint.x, mStartPoint.y);
        mPath.cubicTo(
                mControlPoint1.x, mControlPoint1.y,
                mControlPoint2.x, mControlPoint2.y,
                mEndPoint.x, mEndPoint.y);
        canvas.drawPath(mPath, mPaint1);

        //绘制关键点
        canvas.drawPoint(mStartPoint.x, mStartPoint.y, mPaint2);
        canvas.drawPoint(mControlPoint1.x, mControlPoint1.y, mPaint2);
        canvas.drawPoint(mControlPoint2.x, mControlPoint2.y, mPaint2);
        canvas.drawPoint(mEndPoint.x, mEndPoint.y, mPaint2);

        
    }
}

效果:
在这里插入图片描述

验证曲线: https://cubic-bezier.com/#.2,.2,.1,1
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值