一、贝塞尔曲线

1. 定义

贝塞尔曲线,是应用于二维图形应用程序的数学曲线,由线段和节点组成,节点是可拖动的支点,线段像可伸缩的皮筋。

2. 公式与效果

(1)线性贝塞尔曲线

b2fb1910f8f9

公式

b2fb1910f8f9

效果

(2)二阶贝塞尔曲线

b2fb1910f8f9

公式

b2fb1910f8f9

效果

(3)三阶贝塞尔曲线

b2fb1910f8f9

公式

b2fb1910f8f9

效果

(4)n阶贝塞尔曲线

b2fb1910f8f9

公式

二、绘制曲线

自定义View,绘制曲线,代码如下:

//自定义View

public class CurveView extends View {

private Path mPath;

private Paint mPaint;

public CurveView(Context context) {

super(context);

init();

}

public CurveView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

init();

}

public CurveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

init();

}

private void init() {

mPath = new Path();

mPath.moveTo(300, 300);

mPath.lineTo(500, 300);

mPath.quadTo(700, 500, 500, 700); //二阶贝塞尔曲线

mPath.lineTo(300, 700);

mPath.quadTo(100, 500, 300, 300);

mPaint = new Paint();

mPaint.setColor(Color.BLUE);

mPaint.setStyle(Paint.Style.STROKE); //设置为空心

mPaint.setStrokeWidth(5);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

canvas.drawPath(mPath, mPaint);

}

}

//布局

android:layout_width="match_parent"

android:layout_height="match_parent">

android:layout_width="wrap_content"

android:layout_height="wrap_content" />

//代码,MAinActivity

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

三、自定义曲线路径动画

//贝塞尔曲线类

public class BesselCurve {

public static float oneBesselCurve(float t, float start, float end) {

float result = (1 - t) * start + t * end;

return result;

}

public static float twoBesselCurve(float t, float start, float control1, float end) {

float result = (1 - t) * (1 - t) * start + 2 * t * (1 - t) * control1 + t * t * end;

return result;

}

public static float threeBesselCurve(float t, float start, float control1, float control2, float end) {

float result = start * (1 - t) * (1 - t) * (1 - t) + 3 * control1 * t * (1 - t) * (1 - t) + 3 * control2 * t * t *(1 - t) + end * t * t * t;

return result;

}

}

//路径点坐标

public class PathPoint {

public static final int PATH_MOVE = 0;

public static final int PATH_ONE_BESSEL = 1;

public static final int PATH_TWO_BESSEL = 2;

public static final int PATH_THREE_BESSEL = 3;

private int operation;

private float endX, endY;

private float control1X, control1Y, control2X, control2Y;

//move操作跟一阶贝塞尔操作都通过该构造方法创建终点坐标

public PathPoint(int operation, float endX, float endY) {

this.operation = operation;

this.endX = endX;

this.endY = endY;

}

//二阶贝塞尔操作通过该构造方法创建控制点跟终点坐标

public PathPoint(int operation, float control1X, float control1Y, float endX, float endY) {

this.operation = operation;

this.control1X = control1X;

this.control1Y = control1Y;

this.endX = endX;

this.endY = endY;

}

//三阶贝塞尔操作通过该构造方法创建控制点跟终点坐标

public PathPoint(int operation, float control1X, float control1Y, float control2X, float control2Y, float endX, float endY) {

this.operation = operation;

this.control1X = control1X;

this.control1Y = control1Y;

this.control2X = control2X;

this.control2Y = control2Y;

this.endX = endX;

this.endY = endY;

}

public void setOperation(int operation) {

this.operation = operation;

}

public int getOperation() {

return operation;

}

public void setEndX(float endX) {

this.endX = endX;

}

public float getEndX() {

return endX;

}

public void setEndY(float endY) {

this.endX = endX;

}

public float getEndY() {

return endY;

}

public void setControl1X(float control1X) {

this.control1X = control1X;

}

public float getControl1X() {

return control1X;

}

public void setControl1Y(float control1Y) {

this.control1Y = control1Y;

}

public float getControl1Y() {

return control1Y;

}

public void setControl2X(float control2X) {

this.control2X = control2X;

}

public float getControl2X() {

return control2X;

}

public void setControl2Y(float control2Y) {

this.control2Y = control2Y;

}

public float getControl2Y() {

return control2Y;

}

}

//路径集合

public class PathSet {

private List mPathSet = new ArrayList<>();

public void moveTo(float endX, float endY) {

PathPoint pathPoint = new PathPoint(PathPoint.PATH_MOVE, endX, endY);

mPathSet.add(pathPoint);

}

public void oneBesselCurveTo(float endX, float endY) {

PathPoint pathPoint = new PathPoint(PathPoint.PATH_ONE_BESSEL, endX, endY);

mPathSet.add(pathPoint);

}

public void twoBesselCurveTo(float control1X, float control1Y, float endX, float endY) {

PathPoint pathPoint = new PathPoint(PathPoint.PATH_TWO_BESSEL, control1X, control1Y, endX, endY);

mPathSet.add(pathPoint);

}

public void threeBesselCurveTo(float control1X, float control1Y, float control2X, float control2Y, float endX, float endY) {

PathPoint pathPoint = new PathPoint(PathPoint.PATH_THREE_BESSEL, control1X, control1Y, control2X, control2Y, endX, endY);

mPathSet.add(pathPoint);

}

public List getPathSet() {

return mPathSet;

}

public void clear() {

mPathSet.clear();

}

}

//利用贝塞尔曲线类自定义估值器

public class PathEvaluator implements TypeEvaluator {

/**

* @param fraction 动画执行的百分比

* @param startValue 起点

* @param endValue 终点

* @return PathPoint 根据动画执行的百分比计算出当前点的坐标

*/

@Override

public PathPoint evaluate(float fraction, PathPoint startValue, PathPoint endValue) {

float currentX = 0, currentY = 0;

switch(endValue.getOperation()) {

case PathPoint.PATH_MOVE:

currentX = endValue.getEndX();

currentY = endValue.getEndY();

break;

case PathPoint.PATH_ONE_BESSEL:

currentX = BesselCurve.oneBesselCurve(fraction, startValue.getEndX(), endValue.getEndX());

currentY = BesselCurve.oneBesselCurve(fraction, startValue.getEndY(), endValue.getEndY());

break;

case PathPoint.PATH_TWO_BESSEL:

currentX = BesselCurve.twoBesselCurve(fraction, startValue.getEndX(), endValue.getControl1X(), endValue.getEndX());

currentY = BesselCurve.twoBesselCurve(fraction, startValue.getEndY(), endValue.getControl1Y(), endValue.getEndY());

break;

case PathPoint.PATH_THREE_BESSEL:

currentX = BesselCurve.threeBesselCurve(fraction, startValue.getEndX(), endValue.getControl1X(), endValue.getControl2X(), endValue.getEndX());

currentY = BesselCurve.threeBesselCurve(fraction, startValue.getEndY(), endValue.getControl1Y(), endValue.getControl2Y(), endValue.getEndY());

break;

default:

break;

}

return new PathPoint(PathPoint.PATH_MOVE, currentX, currentY);

}

}

//布局

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/main_tv"

android:layout_width="50px"

android:layout_height="50px"

android:background="@color/colorAccent"/>

//代码,MAinActivity

private void startBesselCurveAnimator() {

PathSet pathSet = getPathSet();

//"this"表示属性动画的作用对象为当前Activity对象

//"pathPoint"为自定义属性名,在当前Activity对象中必须要有public访问权限的"setPathPoint()"方法设置"pathPoint"属性值

ObjectAnimator objectAnimator = ObjectAnimator.ofObject(this, "pathPoint", new PathEvaluator(), pathSet.getPathSet().toArray());

objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());

objectAnimator.setRepeatCount(2);

objectAnimator.setRepeatMode(ObjectAnimator.REVERSE);

objectAnimator.setDuration(5000);

objectAnimator.start();

}

private PathSet getPathSet() {

PathSet pathSet = new PathSet();

pathSet.moveTo(300,300);

pathSet.oneBesselCurveTo(500, 300);

pathSet.twoBesselCurveTo(700, 500, 500, 700);

pathSet.oneBesselCurveTo(300, 700);

pathSet.twoBesselCurveTo(100, 500, 300, 300);

return pathSet;

}

public void setPathPoint(PathPoint pathPoint) { //自定义属性"pathPoint"的设置方法,具有public访问权限

mMain_tv.setTranslationX(pathPoint.getEndX()); //设置TextView的x轴方向的偏移量

mMain_tv.setTranslationY(pathPoint.getEndY()); //设置TextView的y轴方向的偏移量

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

initView();

startBesselCurveAnimator();

}

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐