贝塞尔曲线弹性绳模拟伸长和收缩

第一次写博客,有任何建议和评论请多多担待~~

看到二阶贝塞尔曲线时觉得很神奇,所以想模拟模拟绳子收缩效果,当AC点固定时:

取消固定之后能自由移动ABC三个点


非固定状态自能移动ABC点 不发生回弹效果。

我们再进行,回弹运动轨迹查看。 - -其实就是B点到AC中线直接的轨迹:


思路当然是:利用AC两点B点为控制点画贝塞尔曲线

            bezier.moveTo(pointA.x, pointA.y);
            bezier.quadTo(pointB.x, pointB.y, pointC.x, pointC.y);
ondraw中的代码,重绘时执行:
   @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(width/2,height/2);
            bezier.reset();
            bezier.moveTo(pointA.x, pointA.y);
            bezier.quadTo(pointB.x, pointB.y, pointC.x, pointC.y);
            canvas.drawPoint(pointA.x, pointA.y, point_paint);
            canvas.drawText("A", pointA.x - 5, pointA.y - 10, text_paint);
            canvas.drawPoint(pointC.x, pointC.y, point_paint);
            canvas.drawText("C", pointC.x - 5, pointC.y - 10, text_paint);
            canvas.drawPoint(pointB.x, pointB.y, point_paint);
            canvas.drawText("B", pointB.x - 5, pointB.y - 10, text_paint);
            canvas.drawPath(bezier, rope_paint);
            canvas.drawLine(pointA.x, pointA.y, pointB.x, pointB.y, line_paint);
            canvas.drawLine(pointC.x, pointC.y, pointB.x, pointB.y, line_paint);
//            测试轨迹用线
            canvas.drawPath(Test, line_paint);
    }
当处于非固定时,移动坐标点该怎么做呢?

将所有的点都放到pointFs这个List里面,当手指按下时,用event获取到当前点击位置,再遍历list寻找是否有点在用户触摸点的正负20范围内,

如果有就将这个点记录下来(赋给lastpoint),退出循环。当手指移动时改变lastpoint的坐标,并且重新绘制。那么这样就做到了更改位置。

public void UnFixed(MotionEvent event)
    {
        switch (event.getAction())
        {
//            这里按下之后 就把最后一次的点保存下来
//            移动时就直接用最后一个点重新定位操作
            case MotionEvent.ACTION_DOWN:
                for (PointF pointF:pointFS)
                {
                    if (pointF.x>=(event.getX()-width/2-20)&&pointF.x<=(event.getX()-width/2+20)
                            &&pointF.y>=(event.getY()-height/2-20)&&pointF.y<=(event.getY()-height/2+20))
                    {
                        lastPoint=pointF;
                    }
                    if (pointF.x>=(event.getX()-width/2-20)&&pointF.x<=(event.getX()-width/2+20)
                            &&pointF.y>=(event.getY()-height/2-20)&&pointF.y<=(event.getY()-height/2+20))
                        break;
                }
                break;
            case MotionEvent.ACTION_MOVE:
            setXY(lastPoint,event);//自定义方法 break;
        }
    }
回弹效果,当手指离开屏幕时,获取ABC三点坐标,开启子线程进行等间隔休眠(可能是我弄麻烦了),获得一个非均匀变化(0-1)的值,模拟弹性回弹

的时候收缩速度越来越快。

 @Override
                public void run() {
                        while (true) {
                            Thread.sleep(5);
                         i=(float)(i*1.05);}]
收缩我是将B点回弹至AC中点的一次函数轨迹,可随意。值得注意的是绘制需要在主线程进行处理,异步处理。

最后附上源代码,楼主小白,多谢谢一定会改善的:

package com.dragon.blogbezier.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by 40774 on 2017/12/19.
 */

public class SecondOrderBezier extends View {
    private int width=0;
    private int height=0;
    private Path bezier;
    private PointF pointB;
    private PointF pointA;
    private PointF pointC;
//    测试路径是否正确
    private Path Test;
//    数组点
    private List<PointF> pointFS;
//    记住移动的最后一个点
    private PointF lastPoint;
//    是否固定AC点
    private boolean isFixed=true;
//    绳子画笔
    private Paint rope_paint;
//    点
    private Paint  point_paint;
//    文本
    private Paint  text_paint;
//    线
    private Paint  line_paint;
    private volatile float i=0;

//    异步处理
private static Handler handler ;
    public SecondOrderBezier(Context context) {
        super(context);
        initAll();
    }
    public SecondOrderBezier(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initAll();
    }
    public SecondOrderBezier(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAll();
    }
   private void initAll()
   {
       initPath();
   }
private void initPath()
{
    handler= new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
                if (msg.what==1)
                    invalidate();
        }
    };
    Test=new Path();
    pointFS=new ArrayList<>();
    rope_paint =new Paint();
    point_paint=new Paint();
    rope_paint.setStyle(Paint.Style.STROKE);
    rope_paint.setAntiAlias(true);
    rope_paint.setColor(Color.GREEN);
    rope_paint.setStrokeWidth((float)4.0);
    point_paint.setStyle(Paint.Style.STROKE);
    point_paint.setAntiAlias(true);
    point_paint.setColor(Color.RED);
    point_paint.setStrokeWidth((float)7.0);
    text_paint=new Paint();
    text_paint.setColor(Color.BLACK);
    text_paint.setStrokeWidth((float)1.0);
    text_paint.setStyle(Paint.Style.STROKE);
    text_paint.setAntiAlias(true);
    line_paint=new Paint();
    line_paint.setColor(Color.GRAY);
    line_paint.setStrokeWidth((float)0.5);
    line_paint.setStyle(Paint.Style.STROKE);
    line_paint.setAntiAlias(true);
    pointA=new PointF();
    pointB=new PointF(0,0);
    pointC=new PointF();
    bezier=new Path();
    pointA.set(-100,0);
    pointC.set(100,0);
    pointFS.add(pointA);
    pointFS.add(pointB);
    pointFS.add(pointC);
    lastPoint=pointB;
}
    @Override
    public boolean onTouchEvent(MotionEvent event) {
//        我突然想到画布进行了移动 导致点击位置发生了改变
//        画布向x增加了 y也增加了
        if(isFixed) {
//            处于固定状态
             OnFixed(event);
        }
       else
        {
//            未固定状态
            UnFixed(event);
        }
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(width/2,height/2);
            bezier.reset();
            bezier.moveTo(pointA.x, pointA.y);
            bezier.quadTo(pointB.x, pointB.y, pointC.x, pointC.y);
            canvas.drawPoint(pointA.x, pointA.y, point_paint);
            canvas.drawText("A", pointA.x - 5, pointA.y - 10, text_paint);
            canvas.drawPoint(pointC.x, pointC.y, point_paint);
            canvas.drawText("C", pointC.x - 5, pointC.y - 10, text_paint);
            canvas.drawPoint(pointB.x, pointB.y, point_paint);
            canvas.drawText("B", pointB.x - 5, pointB.y - 10, text_paint);
            canvas.drawPath(bezier, rope_paint);
            canvas.drawLine(pointA.x, pointA.y, pointB.x, pointB.y, line_paint);
            canvas.drawLine(pointC.x, pointC.y, pointB.x, pointB.y, line_paint);
//            测试用线
            canvas.drawPath(Test, line_paint);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width=w;
        height=h;
    }
    
    private void setXY(PointF a,MotionEvent event)
    {
        a.x = event.getX() - width / 2;
        a.y = event.getY() - height / 2;
    }
    
    public void setFixedPoint(boolean isFixed)
    {
        this.isFixed=isFixed;
//        改变固定状态 就把最后一点还原
        lastPoint=pointB;
    }
    
    public void OnFixed(MotionEvent event)
    {
        setXY(pointB, event);
        if (event.getAction()==MotionEvent.ACTION_DOWN)
        {
            i=0;
        }
        if (event.getAction()==MotionEvent.ACTION_UP) {
            Test.reset();
//            开启子线程 利用时间间隔模拟弹性收缩时间
            new Thread(new Runnable() {
                float x=(pointA.x+pointC.x)/2;
                float y=(pointA.y+pointC.y)/2;
                float bx=pointB.x;
                float by=pointB.y;
                float k=(by-y)/(bx-x);
                float b=y-x*k;
                @Override
                public void run() {
                    try {
                        i=i+0.1f;
                        Test.moveTo(bx,by);
                        Log.i("计算值:","k="+k+" b="+b);
                        while (true) {
//                                    模拟弹性恢复过程
                            Thread.sleep(5);
//                                   相当于增加刷新次数 减少刷新时间
//                            由于i增加速度不是线性 而是递增所有回弹速度是在增加
                            i=(float)(i*1.05);
                            Log.i("计算值:","i="+i);
//                                 但i最小时 point处于原位置
//                                  i逐渐增加线性靠近 两个中点
                            if(i<1) {
                                float x1=bx-i*(bx-x);
                                pointB.set(x1,k*x1+b );
                                Test.lineTo(pointB.x,pointB.y );
                            }
                            else {
                                pointB.set(x, y);
                            }
                            handler.sendEmptyMessage(1);
                            if(i>=1)
                                break;
                        }
                    }catch (InterruptedException e)
                    {}
                }
            }).start();
        }
    }
    public void UnFixed(MotionEvent event)
    {
        switch (event.getAction())
        {
//            这里按下之后(灵敏度为40*40个像素)就把最后一次的点保存下来
//            移动时就直接用最后一个点重新定位操作
            case MotionEvent.ACTION_DOWN:
                for (PointF pointF:pointFS)
                {
                    if (pointF.x>=(event.getX()-width/2-20)&&pointF.x<=(event.getX()-width/2+20)
                            &&pointF.y>=(event.getY()-height/2-20)&&pointF.y<=(event.getY()-height/2+20))
                    {
                        lastPoint=pointF;
                    }
                    if (pointF.x>=(event.getX()-width/2-20)&&pointF.x<=(event.getX()-width/2+20)
                            &&pointF.y>=(event.getY()-height/2-20)&&pointF.y<=(event.getY()-height/2+20))
                        break;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                setXY(lastPoint,event);
                break;
        }
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值