自定义view绘图篇--贝塞尔曲线

一路升级打怪终于到了贝塞尔曲线啦!学到这里我们就可以搞一些炫酷的特效啦,qq的红点拖动、小说阅读的翻页特效这些都是常见的典例,是不是好期待!!!是不是想开启装X模式,自己亲手实战,别急我们先探讨下本节内容。。。

知识点

在这里插入图片描述

一、贝塞尔曲线简介

1、起源

在数学的数值分析领域中,贝塞尔曲线(英语:Bézier curve,亦作“贝塞尔”)是计算机图形学中相当重要的参数曲线。更高维度的广泛化贝塞尔曲线就称作贝兹曲面,其中贝兹三角是一种特殊的实例。
贝塞尔曲线于1962年,由法国工程师皮埃尔·贝兹(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由保尔·德·卡斯特里奥于1959年运用德卡斯特里奥算法开发,以稳定数值的方法求出贝塞尔曲线。
ps:这看着怎么这么详细?嘿嘿!没错这就是“维基百科” 对贝塞尔曲线的解释

在这里插入图片描述

2、常见场景

1、qq消息红点气泡的拖动
2、阅读app的翻页特效
3、炫酷特效太多啦…

3、分类

一阶贝塞尔曲线
二阶贝塞尔曲线
三阶贝塞尔曲线
n阶。。。。。

二、原理及其要点

须知!!!

1、图片来源:维基百科-贝塞尔曲线
2、数学公式可以忽略,我们主要看原理图(理解)
3、看对应的安卓封装方法(运用)
4、说了一大堆其实就是运用贝塞尔曲线画一条曲线。

1、一阶原理图解

(1)数学公式----看着算啦

给定点P0、P1,线性贝塞尔曲线(一阶贝塞尔曲线)只是一条两点之间的直线。这条线由下式给出:

在这里插入图片描述
(2)原理图

在这里插入图片描述

定点:p0、p1
控制点:没有
结果:一条直线,和我们前面学习的path的lineTo一样,从起点到终点的一条直线。

(3)对应Path方法

对应path的 lineTo方法

2、二阶原理图

(1)数学公式----看着算啦

二次方贝塞尔曲线的路径由给定点P0、P1、P2的函数B(t)追踪:

在这里插入图片描述

(2)原理图

在这里插入图片描述

二阶贝塞尔曲线;
1、定点p0、p2
2、控制点p1

在这里插入图片描述

曲线的得到过程:
如上图D、E、F分别为所在直线的取点。当D从A到B变化(或者B到A)E从B到C变化(或者C到B)F点的位置变化轨迹就形成曲线。

如上我们可以拆分二阶贝塞尔曲线:
1、定点A、B 、 无控制点的一阶贝塞尔曲线
2、定点B、C、无控制点的一阶贝塞尔曲线
3、定点D、E无控制点 的一阶贝塞尔曲线
ps:当D点E点在AB 、BC上变化时,定点D、E无控制点 的一阶贝塞尔曲线就形成了曲线轨迹。

(3)对应Path方法

对应path的 quadTo方法

 /**
     * 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)

画一条贝塞尔曲线,控制点为x1,y1。结束点为x2,y2。如果path使用前没有调用过moveTo那么,起点为上一次path结束点或者默认为原点
参数:x1,y1控制点坐标
参数:x2,y2结束点坐标

3、三阶原理图

(1)数学公式----看着算啦

P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;这两个点只是在那里提供方向资讯。P0和P1之间的间距,决定了曲线在转而趋进P2之前,走向P1方向的“长度有多长”。

在这里插入图片描述

(2)原理图

在这里插入图片描述

(3)对应Path方法

对应path的 cubicTo方法

 /**
     * Add a cubic bezier from the last point, approaching control points
     * (x1,y1) and (x2,y2), and ending at (x3,y3). 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 1st control point on a cubic curve
     * @param y1 The y-coordinate of the 1st control point on a cubic curve
     * @param x2 The x-coordinate of the 2nd control point on a cubic curve
     * @param y2 The y-coordinate of the 2nd control point on a cubic curve
     * @param x3 The x-coordinate of the end point on a cubic curve
     * @param y3 The y-coordinate of the end point on a cubic curve
     */
    public void cubicTo(float x1, float y1, float x2, float y2,
                        float x3, float y3)

参数:x1,y1控制点1坐标
参数:x2,y2控制点2坐标
参数:x3,y3结束点坐标

三、贝塞尔曲线的使用

1、一阶贝塞尔曲线

一阶就是直线,参考path的画直线操作即可。

2、二阶贝塞尔曲线

上文了解:二阶贝塞尔曲线组成
1、两个定点(确定位置)
2、一个控制点(确定曲线弯曲程度)

(1)栗子预览
在这里插入图片描述

(2)代码

/**
 * Created by sunnyDay on 2019/8/2 11:28
 * 二阶贝塞尔曲线
 */
public class Bezier extends View {

    private Paint paint;
    private Point start, end, control;


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

    public Bezier(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Bezier(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();

        // 初始化画笔

        paint.setColor(Color.BLUE);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(8);
        paint.setStyle(Paint.Style.STROKE);
        paint.setTextSize(60);

        // 初始化点
        start = new Point(-200, -200);
        end = new Point(200, 200);
        control = new Point(0, -200);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int centerX = w / 2;
        int centerY = h / 2;

        // 初始化 两定点、控制点坐标
        start.x = centerX - 200;
        start.y = centerY;

        end.x = centerX + 200;
        end.y = centerY;

        control.x = centerX;
        control.y = centerY - 100;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 更新控制点 通知重绘
        control.x = (int) event.getX();
        control.y = (int) event.getY();
        invalidate();
        return true;
    }

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

        paint.setColor(Color.GRAY);
        paint.setStrokeWidth(20);
        // 绘制贝塞尔曲线的起始点 控制点
        canvas.drawPoint(start.x, start.y, paint);
        canvas.drawPoint(end.x, end.y, paint);
        canvas.drawPoint(control.x, control.y, paint);


        paint.setStrokeWidth(4);
        // 绘制定点到控制点的辅助线
        canvas.drawLine(start.x, start.y, control.x, control.y, paint);
        canvas.drawLine(end.x, end.y, control.x, control.y, paint);
        //绘制贝塞尔曲线
        paint.setColor(Color.RED);
        paint.setStrokeWidth(8);

        Path path = new Path(); //此处有坑

        path.moveTo(start.x, start.y); //起点
        path.quadTo(control.x, control.y, end.x, end.y);
        canvas.drawPath(path, paint);

    }
}

1、模仿大佬的到吗实现了一遍
2、代码中的坑,当时自己天真想着onDraw方法不断重绘即Path path = new Path();产生过多的对象岂不浪费,于是就吧对象的初始化放在了构造中结果gg,绘制时path为同一个对象,导致出现了好多曲线。这里需要留意下。。。。。

3、三阶贝塞尔曲线

三阶贝塞尔曲线组成:
1、两个数据点
2、两个控制点

(1)效果

在这里插入图片描述

(2)栗子

package com.example.customview;

import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * Created by sunnyDay on 2019/8/2 11:28
 * 三阶贝塞尔曲线
 */
public class Bezier3 extends View {

    private Paint paint;
    private Point start, end, control, control2;
    private boolean mode = true;


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

    public Bezier3(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Bezier3(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();

        // 初始化画笔

        paint.setColor(Color.BLUE);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(8);
        paint.setStyle(Paint.Style.STROKE);
        paint.setTextSize(60);

        // 初始化点
        start = new Point(0, 0);
        end = new Point(0, 0);
        control = new Point(0, 0);
        control2 = new Point(0,0);
    }



    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int centerX = w / 2;
        int centerY = h / 2;

        // 初始化 两定点、控制点坐标
        start.x = centerX - 200;
        start.y = centerY;

        end.x = centerX + 200;
        end.y = centerY;

        control.x = centerX-200;
        control.y = centerY - 100;

        control2.x = centerX+200;
        control2.y = centerY - 100;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 更新控制点 通知重绘
        if(mode){
            control.x = (int) event.getX();
            control.y = (int) event.getY();
        }else{
            control2.x = (int) event.getX();
            control2.y = (int) event.getY();
        }

        invalidate();
        return true;
    }

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

        paint.setColor(Color.GRAY);
        paint.setStrokeWidth(20);
        // 绘制贝塞尔曲线的起始点 控制点
        canvas.drawPoint(start.x, start.y, paint);
        canvas.drawPoint(end.x, end.y, paint);
        canvas.drawPoint(control.x, control.y, paint);
        canvas.drawPoint(control2.x, control2.y, paint);


        paint.setStrokeWidth(4);
        // 绘制定点到控制点、控制点到控制点的辅助线
        canvas.drawLine(start.x, start.y, control.x, control.y, paint);
        canvas.drawLine(control.x,control.y,control2.x,control2.y,paint);
        canvas.drawLine(control2.x,control2.y, end.x, end.y, paint);


        //绘制贝塞尔曲线
        paint.setColor(Color.RED);
        paint.setStrokeWidth(8);

        Path path = new Path(); //此处有坑

        path.moveTo(start.x, start.y); //起点
        path.cubicTo(control.x, control.y, control2.x,control2.y, end.x, end.y);
        canvas.drawPath(path, paint);

    }
    public void setMode(boolean mode) {
        this.mode = mode;
    }
}

和二阶的使用方式不同:
1、多画了一个定点
2、多画了一个辅助线
3、path调用的方法不同(cubicTo)

4、升阶与降阶

高阶的贝塞尔曲线都可以由低阶的组合来实现

(1)降阶

定义:

保持曲线形状与方向不变
减少控制点数量

结果:

数据点变多
控制点可能减少
灵活性变弱

(2)升阶
定义:

保持曲线形状与方向不变,
增加控制点数量

结果:

数据点不变,
控制点增加,
灵活性变强

四、感悟

理解贝塞尔曲线的原理
理解曲线的生成方式
贝塞尔曲线就是画的曲线直线集合(曲线 的圆、圆弧都可以用贝塞尔曲线画出)

end

参考模仿:
Path之贝塞尔曲线

参考:
Path从懵逼到精通(2)——贝塞尔曲线

自定义view学习推荐参考:

自定义View系列

Android自定义控件三部曲文章索引

两位大佬写的真心不错!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值