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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容