自定义贝塞尔曲线

         有时候现有的控件无法满足我们的需求,打不到我们想要的效果,这时候就需要我们自己来写一个自定义view,来满足自己的需求。
     而贝塞尔曲线,就是其中的一种方式,比如说我们需要画一个弧形的线条或者是图形,这时候就可以使用贝塞尔曲线来进行,最简单的就是画一个圆形。
     下面就是介绍一下贝塞尔曲线:
     贝塞尔曲线,就是通过一个起始点,一个结束点,中间对应不同的数量的条件点组成,每条线的距离可以不同,长度也可以不同,所连接的角度也不同。
     最简单的,就是只有起始点和结束点的,就是一条直线。。。
之后,就是三条线的贝塞尔曲线,根据角度和长度不同可以形成半圆或者是扭曲的圆。
以两条线三点的贝塞尔曲线为例,第一个是起始点,第二个是结束点,中间还有一个条件点,三条线链接形成一个夹角,三个角分为ABC。
然后,我们可以设定为由A到B的时间为0-1,B到C的时间也是0-1,当开始画图的时候,从A到B的点与B到C的点相连成一条线,在这条线上,有个点D以同样的时间进度前进,从A开始,随着进度到C,在这期间所画成的弧线就是贝塞尔曲线。
以图为例,每一条相同颜色 的线,就是连接的线,上面的那一圈黑点就是随着移动在线上的移动位置所经过的位置,当这些点的距离够近并且相连起来的时候,就是一个曲线,每一条的移动距离是不一定的,但是从头到尾的移动时间都是一样的,也就是在0-1之间走完。
比如,那个处于中间位置的蓝色,当A到B的线上走到0.5的位置,那么B到C的距离也一定是0.5,而在那条蓝色的线上的点,处于的位置也一定是0.5,随着增加而增加。
也就是说,所有的点的进度,都是相同的,无论所在的线的长短。
这就是要给二阶的贝塞尔曲线,这样就算是其他更高阶的贝塞尔曲线也是同样的方式处理。
三阶的贝塞尔曲线是三条线,四阶的是四条线:


而运算的方式都是一样的,将两个相近的线相连,三阶的就会成为两条线,在将两条线相连,就会形成第三条线,而第三条线的移动和上面的点经过的位置就是曲线的移动路线,四阶的一样。

这是贝塞尔曲线的运算公式,每次随着增加,运算的平方都会跟着增加,理论上可以无上限,但是我们平时并不会用到那么多就是了,基本上三阶的就够了。



下面就是通过自定义View来实现1~3阶的贝塞尔曲线,因为在系统里直接就有1~3阶的计算公式,我们只要直接调用本身的就可以了。
public class Text_3_2_1 extends View {
public Text_3_2_1(Context context) {
super (context);
init();
}

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

public Text_3_2_1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super (context, attrs, defStyleAttr);
init();
}
// 画笔
private final Paint mPaint = new Paint(Paint. ANTI_ALIAS_FLAG );
private final Path mPath = new Path();

// 初始化方法
private void init(){



Paint paint= mPaint ;


// 画笔设置
// 抗锯齿
paint.setAntiAlias( true );
// 抗抖动
paint.setDither( true );
paint.setStyle(Paint.Style. STROKE );
paint.setStrokeWidth( 5 );


// 一阶的贝塞尔曲线只有两个点
// 起始点
Path path= mPath ;
path.moveTo( 50 , 50 );
// 结束点
path.lineTo( 50 , 800 );


/**
* 二阶贝塞尔曲线,是从上一个的结束, 400 开始的
* 后面的数是结束点,前面的 xy 是中轴线,是上一个的结尾和后面的中间位置
* 这种方式适合自己给定固定好的点
*/
// path.quadTo(300,100,400,200);

/**
* 相对的实现二阶贝塞尔曲线 , 相对于上一次结束的点
* 相对于上一个结束点,先是控制点,对应着结束自己增加与减少,第二个结束点也一样。
* 也就是,用结束点加上数字得到控制点的数字 300+100. 。。
* 这个不用考虑固定的坐标,只要考虑之后的增减就可以,会随着上一个的移动而自己调节
* 适合不固定的点
*/
// path.rQuadTo(100,-100,200,0);
// path.moveTo(200,400);

/**
* 三阶贝塞尔曲线,有两个控制点,这个是给出固定坐标点的
* 第一对是第一个控制点的 x y
* 第二对的控制点的 X Y
* 第三个则是最后的结束点
* 画出一个曲线
*/
// path.cubicTo(300,100,400,400,500,200);
/**
* 相对的实现三阶贝塞尔曲线
* 与二阶的样,进行运算加减
* 50,800)
*/
path.rCubicTo( 100 ,- 400 , 300 ,- 200 , 500 ,- 400 );

}

@Override
protected void onDraw(Canvas canvas) {
super .onDraw(canvas);
// 绘画
canvas.drawPath( mPath , mPaint );
// 打印控制点
canvas.drawPoint( 300 , 100 , mPaint );
canvas.drawPoint( 400 , 400 , mPaint );

}
}


而从四阶以上,就需要我们自己进行计算,因为里面就已经没有公式了,而且因为需要循环计算从0-1之间的移动,消耗的资源也是很多的,下面就是代码。
public class BezierView extends View{
public BezierView(Context context) {
super (context);
init();
}

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

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


private Path mBezier = new Path();
private Paint mPaint = new Paint(Paint. ANTI_ALIAS_FLAG );
public void init(){
Paint paint= mPaint ;
// 画笔设置
// 抗锯齿
paint.setAntiAlias( true );
// 抗抖动
paint.setDither( true );
paint.setStyle(Paint.Style. STROKE );
paint.setStrokeWidth( 10 );

// 初始化贝塞尔曲线 4 ~~ 往上
initBezier();
}
private void initBezier(){
//(0,0),(300,300),(200,700),(500,500),(700,1200)
float [] xPoints= new float []{ 0 , 100 , 200 , 300 , 400 };
float [] yPoints= new float []{ 300 , 600 , 100 , 500 , 300 };

// float pregress=0.2f;//...

Path path= mBezier ;

int fps= 100 ;
for ( int i= 0 ;i<= 100 ;i++){
// 进度
float pregress=i/( float )fps;
float x = calculateBezier(pregress, xPoints);
float y = calculateBezier(pregress, yPoints);
// 使用链接的方式,当 xy 变动足够小的情况下,就是平滑曲线了
Log. e ( "x..." , ".." +x);
Log. e ( "y..." , ".." +y);
path.lineTo(x,y);
}
}

/**
*
* 计算某时刻的贝塞尔所处的值( x Y
* @param t 时间( 0~1
* @param values 贝塞尔点集合( x y
* @return 当前 t 时刻的贝塞尔所处点
*/
private float calculateBezier( float t, float ... values){
// 才用双层循环
// 首先得到当前的长度
final int len=values. length ; // 初始 =4
// 外层就是当前长度 -1 的循环
//i 初始 =3 ,核心部分
for ( int i=len- 1 ;i> 0 ;i--){
// 外层
for ( int j= 0 ;j<i;j++){
// 内层,进行计算
/**
* 两点之间进行计算的公式
* 第一个点 + 后一个点减去第一个点的差值乘以时间 t
* 算出来的再加上第一个点的就是第一个点到第二个点的距离
*/
values[j]=values[j]+(values[j+ 1 ]-values[j])*t;

}
}
// 运算时结构保存在第一位
// 所以,我们返回第一位
return values[ 0 ];
}
@Override
protected void onDraw(Canvas canvas) {
super .onDraw(canvas);
canvas.drawPath( mBezier , mPaint );
}
}




















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值