6.Android 自定义view《六》 贝赛尔曲线和手势轨迹、水波纹效果(从入门到巅峰)

1、手势轨迹

2、水波纹效果

电池充电时,有些手机会显示水波纹效果,就是这样做出来的。 

 

 

贝赛尔曲线公式

主要用到一阶贝赛尔曲线和二阶贝赛尔曲

一阶贝赛尔曲线

总而言之:对于一阶贝赛尔曲线,大家可以理解为在起始点和终点形成的这条直线上,匀速移动的点。 

 

二阶贝赛尔曲

钢笔工具所使用的路径弯曲效果就是二阶贝赛尔曲线。 

 

二、Android中贝赛尔曲线之quadTo

 

1、quadTo使用原理

public void quadTo(float x1, float y1, float x2, float y2)

参数中(x1,y1)是控制点坐标,(x2,y2)是终点坐标 

大家可能会有一个疑问:有控制点和终点坐标,那起始点是多少呢? 

整条线的起始点是通过Path.moveTo(x,y)来指定的,而如果我们连续调用quadTo(),前一个quadTo()的终点,就是下一个quadTo()函数的起点;如果初始没有调用Path.moveTo(x,y)来指定起始点,则默认以控件左上角(0,0)为起始点;

--

 

Path path = new Path();
path.moveTo(100,300);
path.quadTo(200,200,300,300);
path.quadTo(400,400,500,300);

canvas.drawPath(path,paint);

第一个起始点是需要调用path.moveTo(100,300)来指定的,之后后一个path.quadTo的起始点是以前一个path.quadTo的终点为起始点的。
------------------- 

总结:

  • 整条线的起始点是通过Path.moveTo(x,y)来指定的,如果初始没有调用Path.moveTo(x,y)来指定起始点,则默认以控件左上角(0,0)为起始点;
  • 而如果我们连续调用quadTo(),前一个quadTo()的终点,就是下一个quadTo()函数的起点;

 

public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
        case MotionEvent.ACTION_DOWN: {
        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);
        }

 

第一:有关在case MotionEvent.ACTION_DOWN时return true的问题:return true表示当前控件已经消费了下按动作,之后的ACTION_MOVE、ACTION_UP动作也会继续传递到当前控件中;如果我们在case MotionEvent.ACTION_DOWN时return false,那么后序的ACTION_MOVE、ACTION_UP动作就不会再传到这个控件来了。有关动作拦截的知识,后续会在这个系列中单独来讲,大家先期待下吧。 

第二:这里重绘控件使用的是postInvalidate();而我们以前也有用Invalidate()函数的。这两个函数的作用都是用来重绘控件的,但区别是Invalidate()一定要在UI线程执行,如果不是在UI线程就会报错。而postInvalidate()则没有那么多讲究,它可以在任何线程中执行,而不必一定要是主线程。其实在postInvalidate()就是利用handler给主线程发送刷新界面的消息来实现的,所以它是可以在任何线程中执行,而不会出错。而正是因为它是通过发消息来实现的,所以它的界面刷新可能没有直接调Invalidate()刷的那么快。 

所以在我们确定当前线程是主线程的情况下,还是以invalide()函数为主。当我们不确定当前要刷新页面的位置所处的线程是不是主线程的时候,还是用postInvalidate为好; 

这里我是故意用的postInvalidate(),因为onTouchEvent()本来就是在主线程中的,使用Invalidate()是更合适的。

--------------------- 

Path类中的moveTo和lineTo的区别

moveto 是移动起始点

lineto是画线

使用Path.lineTo()所存在问题和Path.quadTo对比

在两个点连接处有明显的转折,而且在S顶部位置横纵坐标变化比较快的位置,看起来跟图片这大后的马赛克一样;利用Path绘图,是不可能出现马赛克的,因为除了Bitmap以外的任何canvas绘图全部都是矢量图,也就是利用数学公式来作出来的图,无论放在多大屏幕上,都不可能会出现马赛克!这里利用Path绘图,在S顶部之所以看起来像是马赛克是因为这个S是由各个不同点之间连线写出来的,而之间并没有平滑过渡,所以当坐标变化比较剧烈时,线与线之间的转折就显得特别明显了。 

所以要想优化这种效果,就得实现线与线之间的平滑过渡,很显然,二阶贝赛尔曲线就是干这个事的。下面我们就利用我们新学的Path.quadTo函数来重新实现下移动轨迹效果。

--------------------- 

使用Path.lineTo()的最大问题就是线段转折处不够平滑。Path.quadTo()可以实现平滑过渡

 

第一句:path.rQuadTo(100,-100,200,0);是建立在(100,300)这个点基础上来计算相对坐标的。 

所以 

控制点X坐标=上一个终点X坐标+控制点X位移 = 100+100=200; 

控制点Y坐标=上一个终点Y坐标+控制点Y位移 = 300-100=200; 

终点X坐标 = 上一个终点X坐标+终点X位移 = 100+200=300; 

终点Y坐标 = 上一个终点Y坐标+控制点Y位移 = 300+0=300; 

所以这句与path.quadTo(200,200,300,300);对等的 

第二句:path.rQuadTo(100,100,200,0);是建立在它的前一个终点即(300,300)的基础上来计算相对坐标的! 

所以 

控制点X坐标=上一个终点X坐标+控制点X位移 = 300+100=200; 

控制点Y坐标=上一个终点Y坐标+控制点Y位移 = 300+100=200; 

终点X坐标 = 上一个终点X坐标+终点X位移 = 300+200=500; 

终点Y坐标 = 上一个终点Y坐标+控制点Y位移 = 300+0=300; 

所以这句与path.quadTo(400,400,500,300);对等的 

最终效果也是一样的。 

通过这个例子,只想让大家明白一点:rQuadTo(float dx1, float dy1, float dx2, float dy2)中的位移坐标,都是以上一个终点位置为基准来做偏移的!

--------------------- 

Path.rQuadTo()

rQuadTo(float dx1, float dy1, float dx2, float dy2)中的位移坐标,都是以上一个终点位置为基准来做偏移的

public void rQuadTo(float dx1, float dy1, float dx2, float dy2)

dx1:控制点X坐标,表示相对上一个终点X坐标的位移坐标,可为负值,正值表示相加,负值表示相减;

dy1:控制点Y坐标,相对上一个终点Y坐标的位移坐标。同样可为负值,正值表示相加,负值表示相减;

dx2:终点X坐标,同样是一个相对坐标,相对上一个终点X坐标的位移值,可为负值,正值表示相加,负值表示相减;

dy2:终点Y坐标,同样是一个相对,相对上一个终点Y坐标的位移值。可为负值,正值表示相加,负值表示相减;

--------------------- 

Path.rQuadTo()和Path.quadTo()

Path.quadTo()是坐标

Path.rQuadTo()是相对坐标

 

 

水波纹的效果:

代码如下:

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要求三阶塞尔曲线上任一点的倾斜角度,可以通过求曲线的导数和切线来得到。 1. 首先,求出三阶塞尔曲线的导数,即: B'(t) = 3(1-t)^2 * (P1 - P0) + 6t(1-t) * (P2 - P1) + 3t^2 * (P3 - P2) 其中,P0、P1、P2、P3 是曲线的四个点,t 是曲线参数,取值范围为 [0,1]。 2. 然后,求出曲线在某一点的切线向量,即曲线在该点的导数向量,例如: - 求曲线在 t=0.5 时的切线向量,可以将 t=0.5 代入导数公式中,得到 B'(0.5)。 - 求曲线在起点 P0 处的切线向量,可以将 t=0 代入导数公式中,得到 B'(0)。 - 求曲线在终点 P3 处的切线向量,可以将 t=1 代入导数公式中,得到 B'(1)。 3. 最后,求出切线向量的倾斜角度,即切线向量和 x 轴正方向之间的夹角,例如: - 求曲线在 t=0.5 时的倾斜角度,可以计算切线向量 B'(0.5) 和 x 轴正方向之间的夹角。 - 求曲线在起点 P0 处的倾斜角度,可以计算切线向量 B'(0) 和 x 轴正方向之间的夹角。 - 求曲线在终点 P3 处的倾斜角度,可以计算切线向量 B'(1) 和 x 轴正方向之间的夹角。 可以使用 Math.atan2(y, x) 方法来计算向量 (x, y) 和 x 轴正方向之间的夹角,例如: ```js // 求曲线在 t=0.5 时的倾斜角度 let t = 0.5; let dx = 3 * (1-t) ** 2 * (P1.x - P0.x) + 6 * t * (1-t) * (P2.x - P1.x) + 3 * t ** 2 * (P3.x - P2.x); let dy = 3 * (1-t) ** 2 * (P1.y - P0.y) + 6 * t * (1-t) * (P2.y - P1.y) + 3 * t ** 2 * (P3.y - P2.y); let angle = Math.atan2(dy, dx); ``` 其中,P0、P1、P2、P3 是曲线的四个点,P0.x、P0.y、P1.x、P1.y、P2.x、P2.y、P3.x、P3.y 分别是这些点的横纵坐标。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值