第一次写博客,有任何建议和评论请多多担待~~
看到二阶贝塞尔曲线时觉得很神奇,所以想模拟模拟绳子收缩效果,当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;
}
}
}