Android繪圖之貝塞爾曲線簡介(15)

Android 繪圖學(xué)習(xí)

1貝塞爾曲線簡介

貝塞爾曲線被發(fā)明用來進(jìn)行汽車的主體設(shè)計(流線型),具體就是通過起始點和終點,以及若干控制點,通過調(diào)整控制點形成的曲線就叫做貝塞爾曲線,很多復(fù)雜一點的特效都需要貝塞爾曲線來實現(xiàn),我們用的比較多的是二維和三維貝塞爾曲線。
應(yīng)用:拋物線,水波紋,平滑曲線。
具體數(shù)學(xué)推導(dǎo)公式我也看不懂,只能給大家貼兩個網(wǎng)上大神制作的貝塞爾曲線生成過程動畫。這里列出的公式后面會用到。
二維貝塞爾曲線公式:


其中P0是開始點,P1是控制點,P2是結(jié)束點。
二維貝塞爾曲線生成示意圖:


三維貝塞爾曲線公式:


其中P0是開始點,P1,P2是控制點,P3是結(jié)束點。
三維貝塞爾曲線示意圖:


貝塞爾曲線在線調(diào)試器: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);
}

參數(shù)講解:
x1,y1:控制點坐標(biāo)
x2,y2:終點坐標(biāo)

rQuadTo簡介:
rQuadTo在前一個點的基礎(chǔ)上相對變換,例如:
// mPath.moveTo(200,700);
// mPath.quadTo(300,300,900,700);
mPath.rQuadTo(100,-400,700,0);
上面quadTo和rQuadTo是相等的,相當(dāng)于從(200,700)利用rQuadTo中的值進(jìn)行變換:
200+100 = 300 ,700-400 = 300,200+700=900,700+0 = 700;

函數(shù)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 貝塞爾曲線的簡單應(yīng)用

平滑曲線
直接使用lineto函數(shù):

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,會有很多銳利的轉(zhuǎn)折,不平滑;

利用二維貝塞爾曲線平滑繪制

如何平滑拖動鼠標(biāo)繪制的點,如果有(A,B,C)三個點,如何平滑的畫出曲線,可以取A,B的中點為起始點,B,C的中點為結(jié)束點,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);
    }
}

利用貝塞爾曲線實現(xiàn)類似拋物線操作:

二階貝塞爾曲線公式:


通過變化t,可以得到貝塞爾曲線的坐標(biāo),

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基礎(chǔ)(2)
Android繪圖之Path(3)
Android繪圖之drawText繪制文本相關(guān)(4)
Android繪圖之Canvas概念理解(5)
Android繪圖之Canvas變換(6)
Android繪圖之Canvas狀態(tài)保存和恢復(fù)(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 動態(tài)修改漸變 GradientDrawable

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容