Android绘图之贝塞尔曲线简介(15)

2019-08-23  本文已影响0人  sliencexiu

Android 绘图学习

1贝塞尔曲线简介

贝塞尔曲线被发明用来进行汽车的主体设计(流线型),具体就是通过起始点和终点,以及若干控制点,通过调整控制点形成的曲线就叫做贝塞尔曲线,很多复杂一点的特效都需要贝塞尔曲线来实现,我们用的比较多的是二维和三维贝塞尔曲线。
应用:抛物线,水波纹,平滑曲线。
具体数学推导公式我也看不懂,只能给大家贴两个网上大神制作的贝塞尔曲线生成过程动画。这里列出的公式后面会用到。
二维贝塞尔曲线公式:


其中P0是开始点,P1是控制点,P2是结束点。
二维贝塞尔曲线生成示意图:


三维贝塞尔曲线公式:


其中P0是开始点,P1,P2是控制点,P3是结束点。
三维贝塞尔曲线示意图:


贝塞尔曲线在线调试器:http://cubic-bezier.com/#.16,.67,.79,.41 http://xuanfengge.com/easeing/ceaser/

2 Android中 quadTo ,rQuadTo 二阶贝塞尔曲线

/**
 * Add a quadratic bezier from the last point, approaching control point
 * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
 * this contour, the first point is automatically set to (0,0).
 *
 * @param x1 The x-coordinate of the control point on a quadratic curve
 * @param y1 The y-coordinate of the control point on a quadratic curve
 * @param x2 The x-coordinate of the end point on a quadratic curve
 * @param y2 The y-coordinate of the end point on a quadratic curve
 */
public void quadTo(float x1, float y1, float x2, float y2) ;

/**
 * Same as quadTo, but the coordinates are considered relative to the last
 * point on this contour. If there is no previous point, then a moveTo(0,0)
 * is inserted automatically.
**/
public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
    isSimplePath = false;
    nRQuadTo(mNativePath, dx1, dy1, dx2, dy2);
}

参数讲解:
x1,y1:控制点坐标
x2,y2:终点坐标

rQuadTo简介:
rQuadTo在前一个点的基础上相对变换,例如:
// mPath.moveTo(200,700);
// mPath.quadTo(300,300,900,700);
mPath.rQuadTo(100,-400,700,0);
上面quadTo和rQuadTo是相等的,相当于从(200,700)利用rQuadTo中的值进行变换:
200+100 = 300 ,700-400 = 300,200+700=900,700+0 = 700;

函数cubicTo、rCubicTo是三阶贝赛尔曲线,暂时不讲解

简单示例

public class ViewDemo16 extends View {

    private int eventX,eventY;
    private int startX,startY;
    private int endX,endY;
    private Paint paint;
    private int screenW;
    private int screenH;

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

    public ViewDemo16(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setAntiAlias(true);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        screenW = w;
        screenH = h;
        startX = screenW/2-400;
        startY = screenH/2;
        endX = screenW/2 + 400;
        endY = screenH/2;
        eventX = screenW/2;
        eventY = screenH/2 - 400;
    }

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

        paint.setColor(Color.BLUE);
        canvas.drawCircle(startX,startY,10,paint);
        canvas.drawCircle(endX,endY,10,paint);
        canvas.drawCircle(eventX,eventY,10,paint);

        paint.setStrokeWidth(10);
        canvas.drawLine(startX,screenH/2,eventX,eventY,paint);
        canvas.drawLine(endX,screenH/2,eventX,eventY,paint);

        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        Path path = new Path();
        path.moveTo(startX,startY);
        path.quadTo(eventX,eventY,endX,endY);
        canvas.drawPath(path,paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                eventX = (int) event.getX();
                eventY = (int) event.getY();
                invalidate();
                break;
        }
        return true;
    }

}

3 贝塞尔曲线的简单应用

平滑曲线
直接使用lineto函数:

public class ViewDemo17 extends View {

    private Paint paint;
    private Path mPath;

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

    public ViewDemo17(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStrokeWidth(15);
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        mPath = new Path();
    }

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

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(mPath,paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN: {
                mPath.reset();
                mPath.moveTo(event.getX(), event.getY());
                return true;
            }
            case MotionEvent.ACTION_MOVE:
                mPath.lineTo(event.getX(), event.getY());
                postInvalidate();
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

}

可以看到直接利用lineto,会有很多锐利的转折,不平滑;

利用二维贝塞尔曲线平滑绘制

如何平滑拖动鼠标绘制的点,如果有(A,B,C)三个点,如何平滑的画出曲线,可以取A,B的中点为起始点,B,C的中点为结束点,B点作为控制点,就可以绘制出和ABC弯曲度方向一样的曲线。

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class ViewDemo17 extends View {

    private Paint paint;
    private Path mPath;
    private float lastX;
    private float lastY;

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

    public ViewDemo17(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStrokeWidth(15);
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        mPath = new Path();
    }

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

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(mPath,paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN: {
                mPath.reset();
                lastX = event.getX();
                lastY = event.getY();
                mPath.moveTo(event.getX(), event.getY());
                return true;
            }
            case MotionEvent.ACTION_MOVE:
                float endx = (event.getX() +lastX)/2;
                float endY = (event.getY() + lastY)/2;
                mPath.quadTo(lastX,lastY,endx,endY);
                lastY = event.getY();
                lastX = event.getX();
                postInvalidate();
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }
}

利用贝塞尔曲线实现类似抛物线操作:

二阶贝塞尔曲线公式:


通过变化t,可以得到贝塞尔曲线的坐标,

public class ViewDemo18 extends View {
    private Paint paint;
    private float startX = 100;
    private float startY = 100;
    private float endX = 1200;
    private float endY = 1990;

    private float controlX = 900;
    private float controlY = 600;

    private float location;

    private Bitmap mBitmap;

    private Path mPath;

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

    public ViewDemo18(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStrokeWidth(15);
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);

        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.jinbi);
        mPath = new Path();
        mPath.moveTo(startX,startY);
        mPath.quadTo(controlX,controlY,endX,endY);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
        valueAnimator.setDuration(3000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                location = (float) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        valueAnimator.start();
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int x = (int) (startX * Math.pow((1 - location), 2) + 2 * controlX * location * (1 - location) + endX * Math.pow(location, 2));
        int y = (int) (startY * Math.pow((1 - location), 2) + 2 * controlY * location * (1 - location) + endY * Math.pow(location, 2));
        canvas.drawBitmap(mBitmap,x,y,paint);
        canvas.drawPath(mPath,paint);

    }
}

android绘图之Paint(1)
android绘图之Canvas基础(2)
Android绘图之Path(3)
Android绘图之drawText绘制文本相关(4)
Android绘图之Canvas概念理解(5)
Android绘图之Canvas变换(6)
Android绘图之Canvas状态保存和恢复(7)
Android绘图之PathEffect (8)
Android绘图之LinearGradient线性渐变(9)
Android绘图之SweepGradient(10)
Android绘图之RadialGradient 放射渐变(11)
Android绘制之BitmapShader(12)
Android绘图之ComposeShader,PorterDuff.mode及Xfermode(13)
Android绘图之drawText,getTextBounds,measureText,FontMetrics,基线(14)
Android绘图之贝塞尔曲线简介(15)
Android绘图之PathMeasure(16)
Android 动态修改渐变 GradientDrawable

上一篇下一篇

猜你喜欢

热点阅读